Introduction
cone rod homeobox (CRX) mutations can cause dominant Leber congenital amaurosis (LCA), a genetic disorder that can lead to blindness. Normally LCA is caused by recessive genes, but on rarer cases, it can be cause by dominant genes, such as CRX. We are look at Bulk RNA-seq data from retinal organoids derived from induced pluripotent stem cells (iPSCs) which is itself take from a LCA patient with CRX-I138fs mutation and their healthy parent. This mutation occurs in the transactivation domain, and so the effect of the mutation ist that CRX is not transcribed. The data source is GSE152939. The author is trying to show that a deno-associated virus gene therapy can restore RNA expression of CRX. However, although this study is about gene therapy, the data that show rescued expression is in single cell RNA-seq data, which is not in the scope of this report, so it was not included. The data was collected from 6 retinal organoids, for 90 days, 125 days, 150 days and 200 day after the iPSCs start to differentiate; 3 of the organoids were from LCA patient, and 3 organoids were from their healthy parent. (Kruczek et al. 2021)
Note that the vast majority of code in this report are take from BCB420 lecture (Isserlin 2022c, 2022a, 2022d, 2022b) with minor modification to make it appropriate for my data.
Note 2: I cannot put bibtex inline citatation within the codechunk, so it is left at the end of the code chunk, and sometimes there is a figure, so the figure legend/caption may be directly followed by the inline citation
Assignment 1 processing
Lets us first do all the processing that was done in assignment 1
source("./assignment_functions.r")
supplementaryFiles <- downloadSupplementaryFiles("GSE152939")
CRX_ExperimentRawCount <- readFile(supplementaryFiles)
CRX_ExperimentRawCountFiltered <- filterOutLowCount(CRX_ExperimentRawCount)
normalized_counts <- normalizeTheCounts(CRX_ExperimentRawCountFiltered)
(Isserlin 2022c, 2022a; Davis and Meltzer 2007; Durinck et al. 2009; Chen, Lun, and Smyth 2016)
ensemblDataSet <- getEnsemblbiomart()
(Isserlin 2022c, 2022a; Durinck et al. 2009)
normalized_counts_annot <- mapTheData(CRX_ExperimentRawCountFiltered, normalized_counts, ensemblDataSet)
(Isserlin 2022c, 2022a; Durinck et al. 2009)
saveRDS(normalized_counts_annot, file = "normalized_counts_annot.rds")
Apprently there is a dupliate emseblem id
normalized_count_data <- normalized_counts_annot[!duplicated(normalized_counts_annot[ , c("ensembl_gene_id")]),]
normalized_counts_annot[duplicated(normalized_counts_annot[ , c("ensembl_gene_id")]),]
NA
Table 1a: The duplicate gene that was removed.
normalized_counts_annot[which(normalized_counts_annot$ensembl_gene_id == 'ENSG00000254876'), ]
Table 1b: showing the two gene has the same ensembl gene id, but not exactly the same hgnc symbol
I find the one that is consider as duplicate, then find the one that has the same enmselble gene id as it. For some reason this ensembl gene id maps to 2 hgnc symbols, SUGT1P4-STRA6LP and STRA6LP, even from the name it look like the same gene
This gene was has a expression of 10 times greater than the 2nd highest expressed gene, it seesm like it may be some error, so I removed it.
normalized_count_cleaned <- normalized_count_data[normalized_count_data$ensembl_gene_id != 'ENSG00000156508', ]
Heatmap for all genes
first make a heat map matrix (the input thing use to generate a heat map)
heatmap_matrix <- normalized_count_cleaned[,
3:ncol(normalized_count_cleaned)]
rownames(heatmap_matrix) <- normalized_count_cleaned$ensembl_gene_id
colnames(heatmap_matrix) <- colnames(normalized_count_cleaned[, 3:ncol(normalized_count_cleaned)])
heatmap_matrix
saveRDS(heatmap_matrix, file = "heatmap_list.rds")
Table 2: The first 1000 row of the filtered normalized data for all samples, ordered by ensembl id
(Isserlin 2022d)
heatmap_matrix <- readRDS("./heatmap_list.rds")
then generate a heat map
library(ComplexHeatmap)
library(circlize)
if(min(heatmap_matrix) == 0){
heatmap_col = colorRamp2(c(0, max(heatmap_matrix)), c( "white", "red"))
} else {
heatmap_col = colorRamp2(c(min(heatmap_matrix), 0, max(heatmap_matrix)), c("blue", "white", "red"))
}
Heatmap(as.matrix(heatmap_matrix[1:1000,]),
show_row_dend = TRUE, show_column_dend = TRUE,
col=heatmap_col, show_column_names = TRUE,
show_row_names = FALSE, show_heatmap_legend = TRUE,
raster_resize_mat = TRUE, heatmap_legend_param = list(title = "Initial Heatmap"))

Figure 1: Initial heatmap for differential expression with the first 1000 genes, order by ensembl id. Only the first 1000 genes are shown due to rendering resource issues (Isserlin 2022d; Gu, Eils, and Schlesner 2016; Gu et al. 2014)
Now with row normalization (i.e. the kind with subtract the mean divided by standard deviation) we can see each gene better
heatmap_matrix <- t(scale(t(heatmap_matrix)))
if(min(heatmap_matrix) == 0){
heatmap_col = colorRamp2(c(0, max(heatmap_matrix)), c( "white", "red"))
} else {
heatmap_col = colorRamp2(c(min(heatmap_matrix), 0, max(heatmap_matrix)), c("blue", "white", "red"))
}
Heatmap(as.matrix(heatmap_matrix[1:1000,]),
show_row_dend = TRUE,show_column_dend = TRUE,
col=heatmap_col,show_column_names = TRUE,
show_row_names = FALSE,show_heatmap_legend = TRUE, heatmap_legend_param = list(title = " Heatmap with row normalization"))

Figure 2: Heatmap for differential expression of the first 1000 genes, ordered by ensembl id, after row normalization. Only the first 1000 genes are shown due to rendering resource issues (Isserlin 2022d; Gu, Eils, and Schlesner 2016; Gu et al. 2014)
you can see that the samples are more similar to each other at the same time point than whether or not it is control, I think that is reasonable since many gene get simulated and repressed during development, so the genes that are on is different from one development time point, to another.
Heatmap for tophits using Limma
We will be fitting the data to the a linear model and using empirical bayes to calculate p-values. Then we will using Benjamni-Hochberg for multiple hypothesis testing, for correcting our p-values.
Simple Model
The simple model is where the model accounts for the condition (control vs experiment), and the day that the data was collected on, but not account for which organoids is the data collected from.
samples <- data.frame(lapply(colnames(CRX_ExperimentRawCountFiltered)[3:ncol(CRX_ExperimentRawCountFiltered)], FUN=function(x){unlist(strsplit(x, split = "\\_"))[c(1, 2, 3)]}))
samples
colnames(samples) <- colnames(CRX_ExperimentRawCountFiltered)[3:ncol(CRX_ExperimentRawCountFiltered)]
rownames(samples) <- c("condition", "time","patient")
samples <- data.frame(t(samples))
# make the model
model_design <- model.matrix(~ samples$condition+samples$time)
# then put everything into a matrix
expressionMatrix <- as.matrix(normalized_count_data[,3:ncol(CRX_ExperimentRawCountFiltered)])
rownames(expressionMatrix) <-
normalized_count_data$ensembl_gene_id
colnames(expressionMatrix) <-
colnames(normalized_count_data)[3:ncol(CRX_ExperimentRawCountFiltered)]
minimalSet <- Biobase::ExpressionSet(assayData=expressionMatrix)
# fit to model
fit <- limma::lmFit(minimalSet, model_design)
# use empirical bayes to calculate p-value
fit2 <- limma::eBayes(fit,trend=TRUE)
topfit <- limma::topTable(fit2,
coef=ncol(model_design),
adjust.method = "BH",
number = nrow(expressionMatrix))
#merge hgnc names to topfit table
output_hits <- merge(normalized_count_data[,1:2],
topfit,
by.y=0,by.x=1,
all.y=TRUE)
#sort by pvalue
output_hits <- output_hits[order(output_hits$P.Value),]
Table 3: Showing for ecah sample, which organoid, at what time, and what condition it is
(Isserlin 2022d; Huber et al. 2015; Ritchie et al. 2015)
knitr::kable(output_hits[1:10,1:7],type="html",row.names = FALSE)
|
ensembl_gene_id
|
hgnc_symbol
|
logFC
|
AveExpr
|
t
|
P.Value
|
adj.P.Val
|
|
ENSG00000225840
|
|
-751.52636
|
278.308984
|
-34.89691
|
0
|
0
|
|
ENSG00000243199
|
|
-14.97061
|
6.890542
|
-30.68555
|
0
|
0
|
|
ENSG00000280614
|
|
-1251.17742
|
468.102238
|
-21.35881
|
0
|
0
|
|
ENSG00000280800
|
|
-1251.17742
|
468.102238
|
-21.35881
|
0
|
0
|
|
ENSG00000281181
|
|
-1251.17742
|
468.102238
|
-21.35881
|
0
|
0
|
|
ENSG00000276043
|
UHRF1
|
19.96635
|
8.419313
|
21.03885
|
0
|
0
|
|
ENSG00000109991
|
P2RX3
|
15.42168
|
4.861319
|
20.92939
|
0
|
0
|
|
ENSG00000151615
|
POU4F2
|
22.61264
|
6.640604
|
20.68397
|
0
|
0
|
|
ENSG00000147432
|
CHRNB3
|
8.65588
|
2.777748
|
20.59896
|
0
|
0
|
|
ENSG00000076003
|
MCM6
|
44.11041
|
47.070545
|
18.54065
|
0
|
0
|
Table 4: Show the limma output after fitting the data to the simple model, calculating the p-value, and correcting the p-value. This is only showing the first 10, ordered by p value. Note that the p value and adjusted p values are not actually 0, they are just rounded to 0
(Isserlin 2022d; Xie 2021)
This is how many genes that have pvalue under 0.05
length(which(output_hits$P.Value < 0.05))
[1] 8178
(Isserlin 2022d)
This is how many genes that still pvalue of less than 0.05 after adjustment
length(which(output_hits$adj.P.Val < 0.05))
[1] 6174
(Isserlin 2022d)
Organoid Model
The organoid model is where the model accounts for the condition (control vs experiment), and the day that the data was collected on, and for which organoids is the data collected from.
model_design_org <- model.matrix(
~ samples$patient + samples$condition + samples$time)
fit_pat <- limma::lmFit(minimalSet, model_design_org)
(Isserlin 2022d; Xie 2021)
Change the model, fit the data to the model, then apply empircal bayes , then correct with BH
fit_org <- limma::lmFit(minimalSet, model_design_org)
fit2_org <- limma::eBayes(fit_org,trend=TRUE)
topfit_org <- limma::topTable(fit2_org,
coef=ncol(model_design_org),
adjust.method = "BH",
number = nrow(expressionMatrix))
#merge hgnc names to topfit table
output_hits_org <- merge(normalized_count_data[,1:2],
topfit_org, by.y=0, by.x=1, all.y=TRUE)
#sort by pvalue
output_hits_org <- output_hits_org[order(output_hits_org$P.Value),]
(Isserlin 2022d; Ritchie et al. 2015)
knitr::kable(output_hits_org[1:10,1:7],type="html",row.names = FALSE)
|
ensembl_gene_id
|
hgnc_symbol
|
logFC
|
AveExpr
|
t
|
P.Value
|
adj.P.Val
|
|
ENSG00000225840
|
|
-751.52636
|
278.308984
|
-34.77848
|
0
|
0
|
|
ENSG00000243199
|
|
-14.97061
|
6.890542
|
-29.37584
|
0
|
0
|
|
ENSG00000151615
|
POU4F2
|
22.61264
|
6.640604
|
21.07068
|
0
|
0
|
|
ENSG00000147432
|
CHRNB3
|
8.65588
|
2.777748
|
21.01946
|
0
|
0
|
|
ENSG00000276043
|
UHRF1
|
19.96635
|
8.419313
|
20.97652
|
0
|
0
|
|
ENSG00000109991
|
P2RX3
|
15.42168
|
4.861319
|
20.54571
|
0
|
0
|
|
ENSG00000280614
|
|
-1251.17742
|
468.102238
|
-20.49586
|
0
|
0
|
|
ENSG00000280800
|
|
-1251.17742
|
468.102238
|
-20.49586
|
0
|
0
|
|
ENSG00000281181
|
|
-1251.17742
|
468.102238
|
-20.49586
|
0
|
0
|
|
ENSG00000165646
|
SLC18A2
|
25.24788
|
8.885919
|
19.54160
|
0
|
0
|
Table 5: Show the limma output after fitting the data to the organoid model, calculating the p-value, and correcting the p-value. This is only showing the first 10, ordered by p-value. Note that the p value and adjusted p values are not actually 0, they are just rounded to 0
(Isserlin 2022d; Xie 2021)
length(which(output_hits_org$P.Value < 0.05))
[1] 7838
(Isserlin 2022d)
length(which(output_hits_org$adj.P.Val < 0.05))
[1] 5677
(Isserlin 2022d)
Comparing both models
There is actually less, but let us look at it from a graph
simple_model_pvalues <- data.frame(ensembl_id =
output_hits$ensembl_gene_id,
simple_pvalue=output_hits$adj.P.Val)
org_model_pvalues <- data.frame(ensembl_id =
output_hits_org$ensembl_gene_id,
organoid_pvalue = output_hits_org$adj.P.Val)
two_models_pvalues <- merge(simple_model_pvalues,
org_model_pvalues,by.x=1,by.y=1)
two_models_pvalues$colour <- "black"
two_models_pvalues$colour[
two_models_pvalues$simple_pvalue<0.05] <- "orange"
two_models_pvalues$colour[
two_models_pvalues$organoid_pvalue<0.05] <- "blue"
two_models_pvalues$colour[
two_models_pvalues$simple_pvalue<0.05 &
two_models_pvalues$organoid_pvalue<0.05] <- "red"
plot(two_models_pvalues$simple_pvalue,
two_models_pvalues$organoid_pvalue,
col = two_models_pvalues$colour,
xlab = "Simple model adjusted p-values",
ylab ="Organoid model adjusted p-values",
main="Simple vs Organoid Limma",
xlim = c(0, 0.1),
ylim = c(0, 0.1))

Figure 3: Showing each gene on a graph where the coordinates is the corrected p-values using the simple model and the organoid model in limma, zoomed in on 0 to 0.1. Highlighting significant genes in both model as red, in simple model only as orange, in organoid model only as red, and non-significant genes as black (Isserlin 2022d)
To determine which model is more appropriate, we take a look at p-value of the gene of interest, CRX.
ensembl_of_interest <- normalized_count_data$ensembl_gene_id[
which(normalized_count_data$hgnc_symbol == "CRX")]
two_models_pvalues$colour <- "grey"
two_models_pvalues$colour[two_models_pvalues$ensembl_id==
ensembl_of_interest] <- "red"
plot(two_models_pvalues$simple_pvalue,
two_models_pvalues$organoid_pvalue,
col = two_models_pvalues$colour,
xlab = "simple model adjusted p-values",
ylab ="organoid model adjusted p-values",
main="Simple vs Organoid Limma",
xlim = c(0, 0.1),
ylim = c(0, 0.1))
points(two_models_pvalues[which(
two_models_pvalues$ensembl_id == ensembl_of_interest),2:3],
pch=20, col="red", cex=1.5)
legend(0,1,legend=c("CRX","rest"),
fill=c("red","grey"),cex = 0.7)

Figure 4: Showing each gene on a graph where the coordinates is the corrected p-values using the simple model and the organoid model in limma, zoomed in on 0 to 0.1. Highlighting only the gene of interest, CRX as red (Isserlin 2022d)
The simple model is better, I believe that is the because there are minimal difference between the retinal organoids, since they are derived from iPSC that is from the same patient.
As well, there is a significant difference between the log FC
output_hits$logFC[output_hits$ensembl_gene_id == ensembl_of_interest]
[1] -280.2248
output_hits_org$logFC[output_hits$ensembl_gene_id == ensembl_of_interest]
[1] -2.577249
Heatmap from limma
Let us look at the heatmap of only the top hits (by corrected pvalue), in the simple model
top_hits <- output_hits$ensembl_gene_id[
output_hits$adj.P.Val<0.05]
heatmap_matrix_tophits <- t(
scale(t(heatmap_matrix[
which(rownames(heatmap_matrix) %in% top_hits),])))
if(min(heatmap_matrix_tophits) == 0){
heatmap_col = colorRamp2(c( 0, max(heatmap_atrix_tophits)),
c( "white", "red"))
} else {
heatmap_col = colorRamp2(c(min(heatmap_matrix_tophits), 0,
max(heatmap_matrix_tophits)), c("blue", "white", "red"))
}
Heatmap(as.matrix(heatmap_matrix_tophits[1:1000,]),
cluster_rows = TRUE,
cluster_columns = TRUE,
show_row_dend = TRUE,
show_column_dend = TRUE,
col=heatmap_col,
show_column_names = TRUE,
show_row_names = FALSE,
show_heatmap_legend = TRUE,
heatmap_legend_param = list(title = "Heatmap from Limma")
)

Figure 5: Heatmap for differential expression of the first 1000 genes, ordered by ensembl id, after row normalization for significant genes by corrected p-value using the simple model in limma. Only the first 1000 genes are shown due to rendering resource issues (Isserlin 2022d)
Let us not cluster the genes but group controls and experiments together
top_hits <- output_hits$ensembl_gene_id[
output_hits$adj.P.Val<0.05]
heatmap_matrix_tophits <- t(
scale(t(heatmap_matrix[
which(rownames(heatmap_matrix) %in% top_hits),])))
heatmap_matrix_tophits <- heatmap_matrix_tophits[,
c(
grep(colnames(heatmap_matrix_tophits), pattern = "\\CRXLCA"),
grep(colnames(heatmap_matrix_tophits), pattern = "\\Control")
)
]
if(min(heatmap_matrix_tophits) == 0){
heatmap_col = colorRamp2(c( 0, max(heatmap_matrix_tophits)),
c( "white", "red"))
} else {
heatmap_col = colorRamp2(c(min(heatmap_matrix_tophits), 0,
max(heatmap_matrix_tophits)),
c("blue", "white", "red"))
}
Heatmap(as.matrix(heatmap_matrix_tophits[1:1000,]),
cluster_rows = TRUE,
cluster_columns = FALSE,
show_row_dend = TRUE,
show_column_dend = TRUE,
col=heatmap_col,
show_column_names = TRUE,
show_row_names = FALSE,
show_heatmap_legend = TRUE,
heatmap_legend_param = list(title = "Limma Heatmap without clustering"))

Figure 6: Heatmap for differential expression of the first 1000 genes, ordered by ensembl id, after row normalization for significant genes based on corrected p-value using the simple model in limma, without clustering, and group control and experiment together, respectively. Only the first 1000 genes are shown due to rendering resource issues (Isserlin 2022d)
We can clearly tell that the test condition do not look like each other. The controls also do not look like each other.
Then again look at top hits with less than 0.01 pvalue
top_hits <- output_hits$ensembl_gene_id[
output_hits$adj.P.Val < 0.01]
heatmap_matrix_tophits <- t(
scale(t(heatmap_matrix[which(rownames(heatmap_matrix) %in% top_hits),])))
heatmap_matrix_tophits <- heatmap_matrix_tophits[,
c(grep(colnames(heatmap_matrix_tophits),pattern = "\\CRXLCA"),
grep(colnames(heatmap_matrix_tophits),pattern = "\\Control"))]
nrow(heatmap_matrix_tophits)
[1] 3726
if(min(heatmap_matrix_tophits) == 0){
heatmap_col = colorRamp2(c( 0, max(heatmap_matrix_tophits)),
c( "white", "red"))
} else {
heatmap_col = colorRamp2(c(min(heatmap_matrix_tophits), 0,
max(heatmap_matrix_tophits)),
c("blue", "white", "red"))
}
Heatmap(as.matrix(heatmap_matrix_tophits[1:1000,]),
cluster_rows = TRUE, show_row_dend = TRUE,
cluster_columns = FALSE,show_column_dend = FALSE,
col=heatmap_col,show_column_names = TRUE,
show_row_names = FALSE,show_heatmap_legend = TRUE, heatmap_legend_param = list(title = "Limma Heatmap, no cluster, 0.1"))

Figure 7: Heatmap for differential expression of the first 1000 genes, ordered by ensembl id, after row normalization for hits with corrected p-value smaller than 0.01 using the simple model in limma, without clustering, and group control and experiment together, respectively. Only the first 1000 genes are shown due to rendering resource issues (Isserlin 2022d; Gu, Eils, and Schlesner 2016; Gu et al. 2014)
Heatmap with tophits from edgeR
Simple Model
The simple model is where the model accounts for the condition (control vs experiment), and the day that the data was collected on, but not account for which organoids is the data collected from.
filtered_data_matrix <- as.matrix(CRX_ExperimentRawCountFiltered[,3:28])
rownames(filtered_data_matrix) <- CRX_ExperimentRawCountFiltered$ens.id
d <- edgeR::DGEList(counts=filtered_data_matrix, group=samples$condition)
d <- edgeR::estimateDisp(d, model_design)
fit <- edgeR::glmQLFit(d, model_design)
(Isserlin 2022d; Chen, Lun, and Smyth 2016)
qlf.pos_vs_neg <- edgeR::glmQLFTest(fit, coef='samples$conditionCRXLCA')
knitr::kable(edgeR::topTags(qlf.pos_vs_neg), type="html",row.names = FALSE)
|
logFC
|
logCPM
|
F
|
PValue
|
FDR
|
|
2.911104
|
3.260820
|
2259.6310
|
0
|
0
|
|
4.776156
|
4.149346
|
1864.2162
|
0
|
0
|
|
-5.335662
|
1.951992
|
1626.9044
|
0
|
0
|
|
8.689098
|
1.065658
|
1399.1244
|
0
|
0
|
|
-3.289784
|
3.956177
|
998.1517
|
0
|
0
|
|
-3.903175
|
3.731926
|
782.7499
|
0
|
0
|
|
-1.658192
|
5.244965
|
608.3937
|
0
|
0
|
|
-2.892034
|
2.697657
|
582.5644
|
0
|
0
|
|
-6.520603
|
4.378680
|
568.5639
|
0
|
0
|
|
-3.219553
|
2.385684
|
530.9328
|
0
|
0
|
|
|
|
x
|
|
samples$conditionCRXLCA
|
|
|
(Isserlin 2022d; Chen, Lun, and Smyth 2016; Xie 2021)
Even though this say FDR, but since we are using the same multiple hypothesis testing method for both edgeR and limma, they are the same type of corrected values. The first one is gene before correction, and second one is after correction
qlf_output_hits <- edgeR::topTags(qlf.pos_vs_neg,sort.by = "PValue",
n = nrow(normalized_count_data))
length(which(qlf_output_hits$table$PValue < 0.05))
[1] 6909
length(which(qlf_output_hits$table$FDR < 0.05))
[1] 4652
(Isserlin 2022d; Chen, Lun, and Smyth 2016)
Organoid model
The organoid Model is where the model accounts for the condition (control vs experiment), and the day that the data was collected on, and for which organoids is the data collected from.
filtered_data_matrix <- as.matrix(CRX_ExperimentRawCountFiltered[,3:28])
rownames(filtered_data_matrix) <- CRX_ExperimentRawCountFiltered$ens.id
d <- edgeR::DGEList(counts=filtered_data_matrix, group=samples$condition)
d_org <- edgeR::estimateDisp(d, model_design_org)
fit_org <- edgeR::glmQLFit(d_org, model_design_org)
(Isserlin 2022d; Chen, Lun, and Smyth 2016)
qlf.pos_vs_neg <- edgeR::glmQLFTest(fit_org, coef='samples$conditionCRXLCA')
knitr::kable(edgeR::topTags(qlf.pos_vs_neg), type="html",row.names = FALSE)
|
logFC
|
logCPM
|
F
|
PValue
|
FDR
|
|
2.911666
|
3.260502
|
1983.9723
|
0
|
0
|
|
-5.334781
|
1.952266
|
1785.2164
|
0
|
0
|
|
4.779307
|
4.149185
|
1630.2907
|
0
|
0
|
|
8.689482
|
1.065724
|
1374.1628
|
0
|
0
|
|
-3.299374
|
3.956445
|
947.1039
|
0
|
0
|
|
-3.907342
|
3.732095
|
741.8239
|
0
|
0
|
|
-1.662314
|
5.244975
|
663.5731
|
0
|
0
|
|
-6.552258
|
4.378782
|
533.3569
|
0
|
0
|
|
-2.891894
|
2.697840
|
516.0534
|
0
|
0
|
|
-6.594928
|
-1.391819
|
495.8771
|
0
|
0
|
|
|
|
x
|
|
samples$conditionCRXLCA
|
|
|
(Isserlin 2022d; Xie 2021; Chen, Lun, and Smyth 2016)
The first one is gene before correction, and second one is after correction
qlf_output_hits_org <- edgeR::topTags(qlf.pos_vs_neg,sort.by = "PValue",
n = nrow(normalized_count_data))
length(which(qlf_output_hits_org$table$PValue < 0.05))
[1] 6556
length(which(qlf_output_hits_org$table$FDR < 0.05))
[1] 4222
(Isserlin 2022d; Chen, Lun, and Smyth 2016)
Compare 2 model from edgeR
simple_model_pvalues_qlf <- data.frame(ensembl_id =
rownames(qlf_output_hits$table),
simple_pvalue = qlf_output_hits$table$FDR)
org_model_pvalues_qlf <- data.frame(ensembl_id =
rownames(qlf_output_hits_org$table),
organoid_pvalue = qlf_output_hits_org$table$FDR)
two_models_pvalues <- merge(simple_model_pvalues_qlf,
org_model_pvalues_qlf, by.x=1, by.y=1)
two_models_pvalues$colour <- "black"
two_models_pvalues$colour[
two_models_pvalues$simple_pvalue<0.05] <- "orange"
two_models_pvalues$colour[
two_models_pvalues$organoid_pvalue<0.05] <- "blue"
two_models_pvalues$colour[
two_models_pvalues$simple_pvalue<0.05 &
two_models_pvalues$organoid_pvalue<0.05] <- "red"
plot(two_models_pvalues$simple_pvalue,
two_models_pvalues$organoid_pvalue,
col = two_models_pvalues$colour,
xlab = "Simple model adjusted p-values",
ylab ="Organoid model adjusted p-values",
main="Simple vs Organoid edgeR",
xlim=c(0, 0.1),
ylim=c(0, 0.1))

Figure 8: Showing each gene on a graph where the coordinates is the corrected p-values using the simple model and the organoid model in glmQLFit, zoomed in on 0-0.1. Highlighting significant genes in both model as red, in simple model only as orange, in organoid model only as red, and non-significant genes as black (Isserlin 2022d)
The majority of genes that have less than 0.05 p-value in either model, has less than 0.05 p-value in both model.
To determine which model is more appropriate, we take a look at p=value of the gene of interest, CRX.
ensembl_of_interest <- normalized_count_data$ensembl_gene_id[
which(normalized_count_data$hgnc_symbol == "CRX")]
two_models_pvalues$colour <- "grey"
two_models_pvalues$colour[two_models_pvalues$ensembl_id==
ensembl_of_interest] <- "red"
plot(two_models_pvalues$simple_pvalue,
two_models_pvalues$organoid_pvalue,
col = two_models_pvalues$colour,
xlab = "simple model adjusted p-values",
ylab ="organoid model adjusted p-values",
main="Simple vs Organoid Limma",
xlim = c(0, 0.01),
ylim = c(0, 0.01))
points(two_models_pvalues[which(
two_models_pvalues$ensembl_id == ensembl_of_interest),2:3],
pch=20, col="red", cex=1.5)
legend(0,1,legend=c("CRX","rest"),
fill=c("red","grey"),cex = 0.7)

Figure 9: Showing each gene on a graph where the coordinates is the corrected p-values using the simple model and the organoid model in glmQLFit, zoomed in on 0-0.01. Highlighting only the gene of interest, CRX (Isserlin 2022d)
The simple model is better, I believe this is still due to the same reason that, there are minimal difference between the retinal organoids, since they are derived from iPSC that is from the same patient.
qlf_output_hits$table$logFC[which(rownames(qlf_output_hits$table) == ensembl_of_interest)]
[1] 0.4778919
qlf_output_hits_org$table$logFC[which(rownames(qlf_output_hits$table) == ensembl_of_interest)]
[1] 0.3961007
Compare limma with quasi-likelihood
qlf_simple_model_pvalues <- data.frame(
ensembl_id = rownames(qlf_output_hits$table),
qlf_simple_pvalue = qlf_output_hits$table$FDR)
limma_simple_model_pvalues <- data.frame(
ensembl_id = output_hits$ensembl_gene_id,
limma_simple_pvalue = output_hits$adj.P.Val)
two_models_pvalues <- merge(qlf_simple_model_pvalues,
limma_simple_model_pvalues,
by.x=1,by.y=1)
two_models_pvalues$colour <- "black"
two_models_pvalues$colour[two_models_pvalues$qlf_simple_pvalue
<0.05] <- "orange"
two_models_pvalues$colour[two_models_pvalues$limma_simple_pvalue
<0.05] <- "blue"
two_models_pvalues$colour[two_models_pvalues$qlf_simple_pvalue
<0.05 &
two_models_pvalues$limma_simple_pvalue<0.05] <- "red"
plot(two_models_pvalues$qlf_simple_pvalue,
two_models_pvalues$limma_simple_pvalue,
col = two_models_pvalues$colour,
xlab = "QLF simple model adjusted p-values",
ylab ="Limma simple model adjusted p-values",
main="QLF vs Limma",
xlim = c(0, 1),
ylim = c(0, 1))

Figure 10: Showing each gene on a graph where the coordinates is the corrected p-values using the simple model in glmQLFit and in limma. Highlighting significant genes in both model as red, in simple model only as orange, in organoid model only as red, and non-significant genes as black (Isserlin 2022d)
For some reason, this is all over the place
# zoomed in to show
ensembl_of_interest <- normalized_count_data$ensembl_gene_id[
which(normalized_count_data$hgnc_symbol == "CRX")]
two_models_pvalues$colour <- "grey"
two_models_pvalues$colour[two_models_pvalues$ensembl_id
==ensembl_of_interest] <- "red"
plot(two_models_pvalues$qlf_simple_pvalue,
two_models_pvalues$limma_simple_pvalue,
col = two_models_pvalues$colour,
xlab = "QLF simple model adjusted p-values",
ylab ="Limma simple model adjusted p-values",
main="QLF vs Limma",
xlim = c(0, 0.02),
ylim = c(0, 0.02))
points(two_models_pvalues[
two_models_pvalues$ensembl_id==ensembl_of_interest,2:3],
pch=24, col="red", cex=1.5)
Figure 11: Showing each gene on a graph where the coordinates is the corrected p-values using the simple model in glmQLFit and in limma, zoomed in on 0-0.02. Highlighting only the gene of interest, CRX (Isserlin 2022d)
Although limma was made for microarray, and I am using bulk RNA-seq data, which edgeR methods are designed for, in edgeR it shows that CRX is actually upregulated, but it should be down regulated.
Heatmap from edgeR
heatmap, but using tophits from qlf instead of limma
top_hits <- rownames(qlf_output_hits$table)[
qlf_output_hits$table$FDR<0.05]
heatmap_matrix_tophits <- t(
scale(t(heatmap_matrix[which(rownames(heatmap_matrix)
%in% top_hits),])))
nrow(heatmap_matrix_tophits)
if(min(heatmap_matrix_tophits) == 0){
heatmap_col = colorRamp2(c( 0, max(heatmap_matrix_tophits)),
c( "white", "red"))
} else {
heatmap_col = colorRamp2(c(min(heatmap_matrix_tophits), 0,
max(heatmap_matrix_tophits)),
c("blue", "white", "red"))
}
Heatmap(as.matrix(heatmap_matrix_tophits[1:1000,]),
cluster_rows = TRUE,
cluster_columns = TRUE,
show_row_dend = TRUE,
show_column_dend = TRUE,
col=heatmap_col,
show_column_names = TRUE,
show_row_names = FALSE,
show_heatmap_legend = TRUE,
heatmap_legend_param = list(title = "glmQLFit Heatmap")
)
Figure 12: Heatmap for differential expression of the first 1000 genes, ordered by ensembl id, after row normalization for significant genes using the simple model in glmQLFit. Only the first 1000 genes are shown due to rendering resource issues (Isserlin 2022d; Gu, Eils, and Schlesner 2016; Gu et al. 2014)
compare to the heatmap from lemma, the day 200 do not cluster together, but the day 150 and day 125 cluster into 2 groups. However all day 90 do cluster together.
then also cluster by control vs experiment
top_hits <- rownames(qlf_output_hits$table)[qlf_output_hits$table$FDR
<0.05]
heatmap_matrix_tophits <- t(
scale(t(heatmap_matrix[which(rownames(heatmap_matrix)
%in% top_hits),])))
heatmap_matrix_tophits<- heatmap_matrix_tophits[,
c(grep(colnames(heatmap_matrix_tophits),pattern = "\\CRXLCA"),
grep(colnames(heatmap_matrix_tophits),pattern = "\\Control"))]
nrow(heatmap_matrix_tophits)
if(min(heatmap_matrix_tophits) == 0){
heatmap_col = colorRamp2(c( 0, max(heatmap_matrix_tophits)),
c( "white", "red"))
} else {
heatmap_col = colorRamp2(c(min(heatmap_matrix_tophits), 0,
max(heatmap_matrix_tophits)),
c("blue", "white", "red"))
}
Heatmap(as.matrix(heatmap_matrix_tophits[1:1000,]),
cluster_rows = TRUE,
cluster_columns = FALSE,
show_row_dend = TRUE,
show_column_dend = FALSE,
col=heatmap_col,
show_column_names = TRUE,
show_row_names = FALSE,
show_heatmap_legend = TRUE,
heatmap_legend_param = list(title = "glmQLFit Heatmap no cluster"))
Figure 13: Heatmap for differential expression of the first 1000 genes, ordered by ensembl id, after row normalization for significant genes using the simple model in glmQLFit, without clustering, and group control and experiment together, respectively. Only the first 1000 genes are shown due to rendering resource issues (Isserlin 2022d; Gu, Eils, and Schlesner 2016; Gu et al. 2014)
Over representation analysis
This is the number of genes upregulated and downregulated (I did check the model design, control was labeled as 0 and experiment/CRX was labeled as 1), so this does mean that that is upregulated and downregulated in experiment.
length(which(output_hits$adj.P.Val < 0.05
& output_hits$logFC > 0))
[1] 3407
length(which(output_hits$adj.P.Val < 0.05
& output_hits$logFC < 0))
[1] 2767
(Isserlin 2022b)
I only outputted the first 5000 genes, because g:profiler will crash with too many genes.
output_hits[,"rank"] <- (-log(output_hits$adj.P.Val, base =10) * sign(output_hits$logFC))
output_hits <- output_hits[order(output_hits$rank),]
upregulated_genes <- output_hits$ensembl_gene_id[
which(output_hits$adj.P.Val < 0.05
& output_hits$logFC > 0)]
downregulated_genes <- output_hits$ensembl_gene_id[
which(output_hits$adj.P.Val < 0.05
& output_hits$logFC < 0)]
first5000_genes <- output_hits$ensembl_gene_id[c(1:5000)]
if (!file.exists("data")) {
dir.create("data")
}
write.table(x=upregulated_genes,
file=file.path("data","CRX_upregulated_genes.txt"), sep = "\t",
row.names = FALSE, col.names = FALSE, quote = FALSE)
write.table(x=downregulated_genes,
file=file.path("data","CRX_downregulated_genes.txt"),sep = "\t",
row.names = FALSE, col.names = FALSE, quote = FALSE)
write.table(x=first5000_genes,
file=file.path("data","CRX_ranked_genelist.txt"),
sep = "\t",
row.names = FALSE,
col.names = FALSE,
quote = FALSE)
(Isserlin 2022b)
Results of G:profiler
I only included geneset that have between 5-200 genes



Volcano Plot
pvalues <- data.frame(
ensembl_id = output_hits$ensembl_gene_id,
pvalues = -log10(output_hits$adj.P.Val))
FC <- data.frame(
ensembl_id = output_hits$ensembl_gene_id,
FC = output_hits$logFC)
pvalue_FC <- merge(pvalues,
FC,
by.x=1,by.y=1)
pvalue_FC$colour <- "black"
pvalue_FC$colour[which(pvalue_FC$pvalues > 0)] <- "blue"
pvalue_FC$colour[which(pvalue_FC$FC < 0)] <- "red"
plot(pvalue_FC$pvalues,
pvalue_FC$FC,
col = pvalue_FC$colour,
xlab = "QLF -log10 adjusted pvalue",
ylab ="QLF log fold changes",
main="Volcano plot from limma",
xlim = c(0, 10),
ylim = c(-5, 5))

Figure 14: volcano plot of all genes, zoomed in to -5 to 5 for logFC, and 0 to 10 for FDR to show the characteristic U shape.
There is visual separation of upregulated genes and downregulated gene
pvalues_int <- data.frame(
ensembl_id = output_hits$ensembl_gene_id,
pvalues = -log10(output_hits$adj.P.Val))
FC_int <- data.frame(
ensembl_id = output_hits$ensembl_gene_id,
FC = output_hits$logFC)
pvalue_FC_interest <- merge(pvalues_int,
FC_int,
by.x=1,by.y=1)
pvalue_FC_interest$colour <- "grey"
pvalue_FC_interest$colour[pvalue_FC$ensembl_id==ensembl_of_interest] <- "red"
plot(pvalue_FC_interest$pvalues,
pvalue_FC_interest$FC,
col = pvalue_FC_interest$colour,
xlab = "QLF -log10 adjusted pvalue",
ylab ="QLF log fold changes",
main="Volcano plot")

Figure 15: volcano plot of all genes, not zoomed in to show that gene of interest is not expressed
Discussion
We have 3 factors that we can include in our model design. The day that the day was collected, which organoid was it collected from, is it experiment or control that we are collecting from?
We do have to include the last one (experiment vs control), or else we are not getting the differential expression for the question that the authors is asking. (Kruczek et al. 2021). Although at first, the control and experiments do not differ that much, but by day 200 they the controls cluster together more than the organoids, so is the experiments. I believe we can conclude that, by day 200, the difference from CRX, is more than the organoid variability.
Looking at the MDS plot from A1, we can tell that the day it was collected from changes the data by a lot, since the RNA is taken from retinal organoids in development, and gene expression for cell in development changes from stage to stage. Therefore we should include this.
Still look at the MDS plot from A1, which organoid it was collected from does not make much of a difference, this is probably because that all control organoids are from the same person, and all experiment organoids are from the same person.
Questions
Calculate p-values for each of the genes in your expression set. How many genes were significantly differentially expressed? What thresholds did you use and why?
In the model that does not account for organoid variability, the number of genes that where significantly differentially express was 8178. In the model that account for organoid variability, the number is 7838. From edgeR, we got 6556 genes under threshold.
I used the threshold of 0.05, because of convention and I do not have additional knowledge (from the original paper or anywhere) about what threshold would give me best results for bulk RNA seq data from retinal organoids derived from iPSC where the testing condition is CRX-LCA and the specific mutation is I138fs48.
Multiple hypothesis testing - correct your p-values using a multiple hypothesis correction method. Which method did you use? And Why? How many genes passed correction?
I tried using Benjamni - Hochberg from both limma and edgeR to correct the p-value for my genes. The one that I would go with is the BH from limma in the simple model, since the simple model is a lot better in log FC and the result are expected unlike in edgeR its CRX is actually upregulated instead of downregulated.. For number of genes passed correction, limma gave me 6174 and 5677, for the 2 models respectively, edgeR gave me 4222. I used Benjamni - Hochberg, because that seems like it is the most widely used, and that was the default method for multiple hypothesis correcting in both limma toptable and edgeR toptags.
Show the amount of differentially expressed genes using an MA Plot or a Volcano plot. Highlight genes of interest.
see volcano plot section
Visualize your top hits using a heatmap. Do you conditions cluster together? Explain why or why not.
My conditions do not cluster together in the sense that the test conditions and the controls do not. However, the days (e.g. expression on day 90 vs expression on day 200 vs expression on day 125/150), do cluster together, I believe this is because during development many genes get expressed and repressed, so that changes in the genes expression due to development overshadows those that are due to the disease (LCA). As well, day 125 and 150 are too close in development, compare to the other two, so there is less of a difference.
What method did you choose and why?
I choose g:profiler as the method for over representation analysis, since I already have experience using it from the g:profiler assignment (which makes it easier to use) and Professor Isserlin seems to recommend it due to the fact that she asked us to use g:profiler for the homework assignment. As well, the pvalue is shown very clearly on g:profiler with lots of colors and the result are quite interpretable
What annotation data did you use and why? What version of the annotation are you using?
I used Gene ontology: Biological Process released 2021-12-15, Reactome released 2022-1-3, Wiki Pathways released 2021-12-10. I am using this since this was recommend in the g:profiler assignment.
How many genesets were returned with what thresholds?
For first 5000 genes on the whole list (I could not ran all 19000 genes, since g:profiler will crash) in a ordered query, the number of terms return at less than 0.05 pvalue and have between 5-200 genes is 33 for Gene ontology: Biological Process, 0 for Reactome, 0 for Wiki Pathways
Run the analysis using the up-regulated set of genes, and the down-regulated set of genes separately. How do these results compare to using the whole list (i.e all differentially expressed genes together vs. the up-regulated and down regulated differentially expressed genes separately)?
the downregulated is really similar to the whole list For the downregulated genes ordered query, the number of terms return at less than 0.05 pvalue and have between 5-200 genes is 32 for Gene ontology: Biological Process, 0 for Reactome, 0 for Wiki Pathways. For the upregulated genes ordered query, the number of terms return at less than 0.05 pvalue and have between 5-200 genes is 441 for Gene ontology: Biological Process, 8 for Reactome, 0 for Wiki Pathways.
Do the over-representation results support conclusions or mechanism discussed in the original paper?
It does, the top two term of GO:BP (for terms that have between 5-200 genes) for the downregulated order genes is detection of light stimulus and photoreceptor cell differentitaion. and the first half of the terms all have to do with vision
As well, the top two term (for terms that have between 5-200 genes) for the first 5000 genes on the whole list is detection of light stimulus and photoreceptor cell differentitaion. And the first half of the terms all have to do with vision
Since a mutation in CRX prevents it from being transcribed, the result of up-regulated genes is not in the scope of experiment anyway.
Can you find evidence, i.e. publications, to support some of the results that you see. How does this evidence support your results.
Yes, in (Hennig, Peng, and Chen 2008), say that CRX is central to the transcription factor network that controls photoreceptor development. In both the whole list ordered in the downregulated genes, photoreception is an important term, this means that in the RNA expression of these genes, phototransduction is not activated due to CRX’s role, which is consistent to the finding presented by that study. Moreover, it is actually photoreceptor cell differentiation, which is exactly what CRX controls.
I believe that detection of light stimulus is also supported by this, since light stimulus is essentially photons, and photoreceptor are activated by photons.
References
Chen, Yunshun, Aaron A T Lun, and Gordon K Smyth. 2016.
“From Reads to Genes to Pathways: Differential Expression Analysis of RNA-Seq Experiments Using Rsubread and the edgeR Quasi-Likelihood Pipeline.” F1000Research 5: 1438.
https://doi.org/10.12688/f1000research.8987.2.
Davis, Sean, and Paul Meltzer. 2007. “GEOquery: A Bridge Between the Gene Expression Omnibus (GEO) and BioConductor.” Bioinformatics 14: 1846–47.
Durinck, Steffen, Paul T. Spellman, Ewan Birney, and Wolfgang Huber. 2009. “Mapping Identifiers for the Integration of Genomic Datasets with the r/Bioconductor Package biomaRt.” Nature Protocols 4: 1184–91.
Gu, Zuguang, Roland Eils, and Matthias Schlesner. 2016. “Complex Heatmaps Reveal Patterns and Correlations in Multidimensional Genomic Data.” Bioinformatics.
Gu, Zuguang, Lei Gu, Roland Eils, Matthias Schlesner, and Benedikt Brors. 2014. “Circlize Implements and Enhances Circular Visualization in r.” Bioinformatics 30: 2811–12.
Hennig, Anne K, Guang-Hua Peng, and Shiming Chen. 2008. “Regulation of Photoreceptor Gene Expression by Crx-Associated Transcription Factor Network.” Brain Research 1192: 114–33.
Huber, W., V. J. Carey, R. Gentleman, S. Anders, M. Carlson, B. S. Carvalho, H. C. Bravo, et al. 2015.
“Orchestrating High-Throughput Genomic Analysis with Bioconductor.” Nature Methods 12 (2): 115–21.
http://www.nature.com/nmeth/journal/v12/n2/full/nmeth.3252.html.
Isserlin, Ruth. 2022a.
“Bcb420 - Computational Systems Biology Lecture. Lecture 5 - Data Exploration and Identifier Mapping.” https://q.utoronto.ca/courses/248455/files/19306012?module_item_id=3480061.
———. 2022b.
“Bcb420 - Computational Systems Biology Lecture. Lecture 7 - Annotation Dataset and Intro to Pathway Analysis.” https://q.utoronto.ca/courses/248455/files/18120892?module_item_id=3210881.
———. 2022c.
“Bcb420 - Computational Systems Biology. Lecture 4 - Exploring the Data and Basics of Normalization.” https://q.utoronto.ca/courses/248455/files/19273570?module_item_id=3476594.
———. 2022d.
“Bcb420 - Computational Systems Biology. Lecture 6 - Differential Expression.” https://q.utoronto.ca/courses/248455/files/19309003?module_item_id=3480535.
Kruczek, Kamil, Zepeng Qu, James Gentry, Benjamin R Fadl, Linn Gieser, Suja Hiriyanna, Zachary Batz, et al. 2021. “Gene Therapy of Dominant CRX-Leber Congenital Amaurosis Using Patient Stem Cell-Derived Retinal Organoids.” Stem Cell Reports 16 (2): 252–63.
Ritchie, Matthew E, Belinda Phipson, Di Wu, Yifang Hu, Charity W Law, Wei Shi, and Gordon K Smyth. 2015.
“limma Powers Differential Expression Analyses for RNA-Sequencing and Microarray Studies.” Nucleic Acids Research 43 (7): e47.
https://doi.org/10.1093/nar/gkv007.
Xie, Yihui. 2021.
Knitr: A General-Purpose Package for Dynamic Report Generation in r.
https://yihui.org/knitr/.
LS0tCnRpdGxlOiAnQkNCNDIwIEFzc2lnbm1lbnQgMiBSZXBvcnQ6IERpZmZlcmVudGlhbCBHZW5lIGV4cHJlc3Npb24gYW5kIFByZWxpbWluYXJ5IE9SQScKYXV0aG9yOiAiS2FpIFJlbiBDaGVuIgpzdWJ0aXRsZTogJ0pvdXJuYWwgQXJ0aWNsZTogR2VuZSB0aGVyYXB5IG9mIGRvbWluYW50IENSWC1MZWJlciBjb25nZW5pdGFsIGFtYXVyb3NpcwogIHVzaW5nIHBhdGllbnQgcmV0aW5hbCBvcmdhbm9pZHMnCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogMgogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiAnMicKICAgIGRmX3ByaW50OiBwYWdlZApiaWJsaW9ncmFwaHk6IGEyY2l0YXRpb24uYmliCmxpbmstY2l0YXRpb25zOiB5ZXMKLS0tCgojIEludHJvZHVjdGlvbgpjb25lIHJvZCBob21lb2JveCAoQ1JYKSBtdXRhdGlvbnMgIGNhbiBjYXVzZSBkb21pbmFudCBMZWJlciBjb25nZW5pdGFsIGFtYXVyb3NpcwooTENBKSwgYSBnZW5ldGljIGRpc29yZGVyIHRoYXQgY2FuIGxlYWQgdG8gYmxpbmRuZXNzLiBOb3JtYWxseSBMQ0EgaXMgY2F1c2VkCmJ5IHJlY2Vzc2l2ZSBnZW5lcywgYnV0IG9uIHJhcmVyIGNhc2VzLCBpdCBjYW4gYmUgY2F1c2UgYnkgZG9taW5hbnQgZ2VuZXMsIHN1Y2gKYXMgQ1JYLiBXZSBhcmUgbG9vayBhdCBCdWxrIFJOQS1zZXEgZGF0YSBmcm9tIHJldGluYWwgb3JnYW5vaWRzIGRlcml2ZWQgZnJvbQppbmR1Y2VkIHBsdXJpcG90ZW50IHN0ZW0gY2VsbHMgKGlQU0NzKSB3aGljaCBpcyBpdHNlbGYgdGFrZSBmcm9tIGEgTENBIHBhdGllbnQgCndpdGggQ1JYLUkxMzhmcyBtdXRhdGlvbiBhbmQgdGhlaXIgaGVhbHRoeSBwYXJlbnQuIFRoaXMgbXV0YXRpb24gb2NjdXJzIGluIHRoZQp0cmFuc2FjdGl2YXRpb24gZG9tYWluLCBhbmQgc28gdGhlIGVmZmVjdCBvZiB0aGUgbXV0YXRpb24gaXN0IHRoYXQgQ1JYIGlzIG5vdAp0cmFuc2NyaWJlZC4gVGhlIGRhdGEgc291cmNlIGlzIEdTRTE1MjkzOS4gVGhlIGF1dGhvciBpcyB0cnlpbmcgdG8gc2hvdyB0aGF0IGEKZGVuby1hc3NvY2lhdGVkIHZpcnVzIGdlbmUgdGhlcmFweSBjYW4gcmVzdG9yZSBSTkEgZXhwcmVzc2lvbiBvZiBDUlguIEhvd2V2ZXIsCmFsdGhvdWdoIHRoaXMgc3R1ZHkgaXMgYWJvdXQgZ2VuZSB0aGVyYXB5LCB0aGUgZGF0YSB0aGF0IHNob3cgcmVzY3VlZCBleHByZXNzaW9uCmlzIGluIHNpbmdsZSBjZWxsIFJOQS1zZXEgZGF0YSwgd2hpY2ggaXMgbm90IGluIHRoZSBzY29wZSBvZiB0aGlzIHJlcG9ydCwgc28gaXQKd2FzIG5vdCBpbmNsdWRlZC4gVGhlIGRhdGEgd2FzIGNvbGxlY3RlZCBmcm9tIDYgcmV0aW5hbCBvcmdhbm9pZHMsIGZvciA5MCBkYXlzLAoxMjUgZGF5cywgMTUwIGRheXMgYW5kIDIwMCBkYXkgYWZ0ZXIgdGhlIGlQU0NzIHN0YXJ0IHRvIGRpZmZlcmVudGlhdGU7IDMgb2YgdGhlCm9yZ2Fub2lkcyB3ZXJlIGZyb20gTENBIHBhdGllbnQsIGFuZCAzIG9yZ2Fub2lkcyB3ZXJlIGZyb20gdGhlaXIgaGVhbHRoeSBwYXJlbnQuCltAQ1JYXQoKTm90ZSB0aGF0IHRoZSB2YXN0IG1ham9yaXR5IG9mIGNvZGUgaW4gdGhpcyByZXBvcnQgYXJlIHRha2UgZnJvbSBCQ0I0MjAgbGVjdHVyZQpbQGw0OyBAbDU7IEBsNjsgQGw3XSB3aXRoIG1pbm9yIG1vZGlmaWNhdGlvbiB0byBtYWtlIGl0IGFwcHJvcHJpYXRlIGZvciBteSBkYXRhLgoKTm90ZSAyOiBJIGNhbm5vdCBwdXQgYmlidGV4IGlubGluZSBjaXRhdGF0aW9uIHdpdGhpbiB0aGUgY29kZWNodW5rLCBzbyBpdCBpcyBsZWZ0CmF0IHRoZSBlbmQgb2YgdGhlIGNvZGUgY2h1bmssIGFuZCBzb21ldGltZXMgdGhlcmUgaXMgYSBmaWd1cmUsIHNvIHRoZSBmaWd1cmUKbGVnZW5kL2NhcHRpb24gbWF5IGJlIGRpcmVjdGx5IGZvbGxvd2VkIGJ5IHRoZSBpbmxpbmUgY2l0YXRpb24KCiMgQXNzaWdubWVudCAxIHByb2Nlc3NpbmcgCgpMZXRzIHVzIGZpcnN0IGRvIGFsbCB0aGUgcHJvY2Vzc2luZyB0aGF0IHdhcyBkb25lIGluIGFzc2lnbm1lbnQgMQoKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnNvdXJjZSgiLi9hc3NpZ25tZW50X2Z1bmN0aW9ucy5yIikKc3VwcGxlbWVudGFyeUZpbGVzIDwtIGRvd25sb2FkU3VwcGxlbWVudGFyeUZpbGVzKCJHU0UxNTI5MzkiKQpDUlhfRXhwZXJpbWVudFJhd0NvdW50IDwtIHJlYWRGaWxlKHN1cHBsZW1lbnRhcnlGaWxlcykKQ1JYX0V4cGVyaW1lbnRSYXdDb3VudEZpbHRlcmVkIDwtIGZpbHRlck91dExvd0NvdW50KENSWF9FeHBlcmltZW50UmF3Q291bnQpCm5vcm1hbGl6ZWRfY291bnRzIDwtIG5vcm1hbGl6ZVRoZUNvdW50cyhDUlhfRXhwZXJpbWVudFJhd0NvdW50RmlsdGVyZWQpCmBgYApbQGw0OyBAbDU7IEBnZW9xdWVyeTsgQGJpb21hcnQ7IEBlZGdlcl0KCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmVuc2VtYmxEYXRhU2V0IDwtIGdldEVuc2VtYmxiaW9tYXJ0KCkKYGBgCltAbDQ7IEBsNTsgQGJpb21hcnRdCgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0Kbm9ybWFsaXplZF9jb3VudHNfYW5ub3QgPC0gbWFwVGhlRGF0YShDUlhfRXhwZXJpbWVudFJhd0NvdW50RmlsdGVyZWQsIG5vcm1hbGl6ZWRfY291bnRzLCBlbnNlbWJsRGF0YVNldCkKYGBgCltAbDQ7IEBsNTsgQGJpb21hcnRdCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpzYXZlUkRTKG5vcm1hbGl6ZWRfY291bnRzX2Fubm90LCBmaWxlID0gIm5vcm1hbGl6ZWRfY291bnRzX2Fubm90LnJkcyIpCmBgYAoKCkFwcHJlbnRseSB0aGVyZSBpcyBhIGR1cGxpYXRlIGVtc2VibGVtIGlkCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9Cm5vcm1hbGl6ZWRfY291bnRfZGF0YSA8LSBub3JtYWxpemVkX2NvdW50c19hbm5vdFshZHVwbGljYXRlZChub3JtYWxpemVkX2NvdW50c19hbm5vdFsgLCBjKCJlbnNlbWJsX2dlbmVfaWQiKV0pLF0KYGBgCgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLmNhcD0nVGFibGUgMWE6IFRoZSBkdXBsaWNhdGUgZ2VuZSB0aGF0IHJlbW92ZWQuJ30Kbm9ybWFsaXplZF9jb3VudHNfYW5ub3RbZHVwbGljYXRlZChub3JtYWxpemVkX2NvdW50c19hbm5vdFsgLCBjKCJlbnNlbWJsX2dlbmVfaWQiKV0pLF0KYGBgClRhYmxlIDFhOiBUaGUgZHVwbGljYXRlIGdlbmUgdGhhdCB3YXMgcmVtb3ZlZC4KCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcuY2FwPSdUYWJsZSAxYjogc2hvd2luZyB0aGUgdHdvIGdlbmUgaGFzIHRoZSBzYW1lIGVuc2VtYmwgZ2VuZSBpZCwgYnV0IG5vdCBleGFjdGx5IHRoZSBzYW1lIGhnbmMgc3ltYm9sJ30Kbm9ybWFsaXplZF9jb3VudHNfYW5ub3Rbd2hpY2gobm9ybWFsaXplZF9jb3VudHNfYW5ub3QkZW5zZW1ibF9nZW5lX2lkID09ICdFTlNHMDAwMDAyNTQ4NzYnKSwgXQpgYGAKVGFibGUgMWI6IHNob3dpbmcgdGhlIHR3byBnZW5lIGhhcyB0aGUgc2FtZSBlbnNlbWJsIGdlbmUgaWQsIGJ1dCBub3QgZXhhY3RseSB0aGUgc2FtZSBoZ25jIHN5bWJvbAoKSSBmaW5kIHRoZSBvbmUgdGhhdCBpcyBjb25zaWRlciBhcyBkdXBsaWNhdGUsIHRoZW4gZmluZCB0aGUgb25lIHRoYXQgaGFzIHRoZSBzYW1lIGVubXNlbGJsZSBnZW5lIGlkIGFzIGl0LiAKRm9yIHNvbWUgcmVhc29uIHRoaXMgZW5zZW1ibCBnZW5lIGlkIG1hcHMgdG8gMiBoZ25jIHN5bWJvbHMsIFNVR1QxUDQtU1RSQTZMUCBhbmQgU1RSQTZMUCwgZXZlbiBmcm9tIHRoZSBuYW1lIGl0IGxvb2sgbGlrZSB0aGUgc2FtZSBnZW5lCgoKVGhpcyBnZW5lIHdhcyBoYXMgYSBleHByZXNzaW9uIG9mIDEwIHRpbWVzIGdyZWF0ZXIgdGhhbiB0aGUgMm5kIGhpZ2hlc3QgZXhwcmVzc2VkCmdlbmUsIGl0IHNlZXNtIGxpa2UgaXQgbWF5IGJlIHNvbWUgZXJyb3IsIHNvIEkgcmVtb3ZlZCBpdC4gCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpub3JtYWxpemVkX2NvdW50X2NsZWFuZWQgPC0gbm9ybWFsaXplZF9jb3VudF9kYXRhW25vcm1hbGl6ZWRfY291bnRfZGF0YSRlbnNlbWJsX2dlbmVfaWQgIT0gJ0VOU0cwMDAwMDE1NjUwOCcsIF0KYGBgCgoKCiMgSGVhdG1hcCBmb3IgYWxsIGdlbmVzCgpmaXJzdCBtYWtlIGEgaGVhdCBtYXAgbWF0cml4ICh0aGUgaW5wdXQgdGhpbmcgdXNlIHRvIGdlbmVyYXRlIGEgaGVhdCBtYXApCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCBmaWcuY2FwPSdUYWJsZSAyOiBUaGUgZmlyc3QgMTAwMCByb3cgb2YgdGhlIGZpbHRlcmVkIG5vcm1hbGl6ZWQgZGF0YSBmb3IgYWxsIHNhbXBsZXMsIG9yZGVyZWQgYnkgZW5zZW1ibCBpZCcsIHdhcm5pbmc9RkFMU0V9CgpoZWF0bWFwX21hdHJpeCA8LSBub3JtYWxpemVkX2NvdW50X2NsZWFuZWRbLAogICAgICAgICAgICAgICAgICAgICAgICAzOm5jb2wobm9ybWFsaXplZF9jb3VudF9jbGVhbmVkKV0Kcm93bmFtZXMoaGVhdG1hcF9tYXRyaXgpIDwtIG5vcm1hbGl6ZWRfY291bnRfY2xlYW5lZCRlbnNlbWJsX2dlbmVfaWQKY29sbmFtZXMoaGVhdG1hcF9tYXRyaXgpIDwtIGNvbG5hbWVzKG5vcm1hbGl6ZWRfY291bnRfY2xlYW5lZFssIDM6bmNvbChub3JtYWxpemVkX2NvdW50X2NsZWFuZWQpXSkKaGVhdG1hcF9tYXRyaXgKc2F2ZVJEUyhoZWF0bWFwX21hdHJpeCwgZmlsZSA9ICJoZWF0bWFwX2xpc3QucmRzIikKYGBgClRhYmxlIDI6IFRoZSBmaXJzdCAxMDAwIHJvdyBvZiB0aGUgZmlsdGVyZWQgbm9ybWFsaXplZCBkYXRhIGZvciBhbGwgc2FtcGxlcywgb3JkZXJlZCBieSBlbnNlbWJsIGlkCgpbQGw2XQoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KaGVhdG1hcF9tYXRyaXggPC0gcmVhZFJEUygiLi9oZWF0bWFwX2xpc3QucmRzIikKYGBgCgoKdGhlbiBnZW5lcmF0ZSBhIGhlYXQgbWFwCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcuY2FwPSdGaWd1cmUgMTogSW5pdGlhbCBoZWF0bWFwIGZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiB3aXRoIHRoZSBmaXJzdCAxMDAwIGdlbmVzLCBvcmRlciBieSBlbnNlbWJsIGlkLiBPbmx5IHRoZSBmaXJzdCAxMDAwIGdlbmVzIGFyZSBzaG93biBkdWUgdG8gcmVuZGVyaW5nIHJlc291cmNlIGlzc3Vlcyd9CmxpYnJhcnkoQ29tcGxleEhlYXRtYXApCmxpYnJhcnkoY2lyY2xpemUpCgojIHNwZWNpZnkgdGhlIGNvbG9yLCBuZWdhdGl2ZSBhcyBibHVlLCByZWQgYXMgcG9zaXRpdmUsIHdoaXRlIGFzIHRyYW5zaXRpb24KaWYobWluKGhlYXRtYXBfbWF0cml4KSA9PSAwKXsKICBoZWF0bWFwX2NvbCA9IGNvbG9yUmFtcDIoYygwLCBtYXgoaGVhdG1hcF9tYXRyaXgpKSwgYyggIndoaXRlIiwgInJlZCIpKQoKfSBlbHNlIHsKICBoZWF0bWFwX2NvbCA9IGNvbG9yUmFtcDIoYyhtaW4oaGVhdG1hcF9tYXRyaXgpLCAwLCBtYXgoaGVhdG1hcF9tYXRyaXgpKSwgYygiYmx1ZSIsICJ3aGl0ZSIsICJyZWQiKSkKfQoKSGVhdG1hcChhcy5tYXRyaXgoaGVhdG1hcF9tYXRyaXhbMToxMDAwLF0pLAogICAgICBzaG93X3Jvd19kZW5kID0gVFJVRSwgc2hvd19jb2x1bW5fZGVuZCA9IFRSVUUsCiAgICAgIGNvbD1oZWF0bWFwX2NvbCwgc2hvd19jb2x1bW5fbmFtZXMgPSBUUlVFLAogICAgICBzaG93X3Jvd19uYW1lcyA9IEZBTFNFLCBzaG93X2hlYXRtYXBfbGVnZW5kID0gVFJVRSwKICAgICAgcmFzdGVyX3Jlc2l6ZV9tYXQgPSBUUlVFLCBoZWF0bWFwX2xlZ2VuZF9wYXJhbSA9IGxpc3QodGl0bGUgPSAiSW5pdGlhbCBIZWF0bWFwIikpCmBgYApGaWd1cmUgMTogSW5pdGlhbCBoZWF0bWFwIGZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiB3aXRoIHRoZSBmaXJzdCAxMDAwIGdlbmVzLCBvcmRlciBieSBlbnNlbWJsIGlkLiBPbmx5IHRoZSBmaXJzdCAxMDAwIGdlbmVzIGFyZSBzaG93biBkdWUgdG8gcmVuZGVyaW5nIHJlc291cmNlIGlzc3VlcwpbQGw2OyBAaGVhdG1hcDsgQGNvbG9yXQoKCgpOb3cgd2l0aCByb3cgbm9ybWFsaXphdGlvbiAoaS5lLiB0aGUga2luZCB3aXRoIHN1YnRyYWN0IHRoZSBtZWFuIGRpdmlkZWQgYnkgc3RhbmRhcmQgZGV2aWF0aW9uKQp3ZSBjYW4gc2VlIGVhY2ggZ2VuZSBiZXR0ZXIKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy5jYXA9J0ZpZ3VyZSAyOiBIZWF0bWFwIGZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBvZiB0aGUgZmlyc3QgMTAwMCBnZW5lcywgb3JkZXJlZCBieSBlbnNlbWJsIGlkLCBhZnRlciByb3cgbm9ybWFsaXphdGlvbi4gT25seSB0aGUgZmlyc3QgMTAwMCBnZW5lcyBhcmUgc2hvd24gZHVlIHRvIHJlbmRlcmluZyByZXNvdXJjZSBpc3N1ZXMnfQpoZWF0bWFwX21hdHJpeCA8LSB0KHNjYWxlKHQoaGVhdG1hcF9tYXRyaXgpKSkKCiMgc3BlY2lmeSB0aGUgY29sb3IsIG5lZ2F0aXZlIGFzIGJsdWUsIHJlZCBhcyBwb3NpdGl2ZSwgd2hpdGUgYXMgdHJhbnNpdGlvbgppZihtaW4oaGVhdG1hcF9tYXRyaXgpID09IDApewpoZWF0bWFwX2NvbCA9IGNvbG9yUmFtcDIoYygwLCBtYXgoaGVhdG1hcF9tYXRyaXgpKSwgYyggIndoaXRlIiwgInJlZCIpKQp9IGVsc2UgewpoZWF0bWFwX2NvbCA9IGNvbG9yUmFtcDIoYyhtaW4oaGVhdG1hcF9tYXRyaXgpLCAwLCBtYXgoaGVhdG1hcF9tYXRyaXgpKSwgYygiYmx1ZSIsICJ3aGl0ZSIsICJyZWQiKSkKfQoKSGVhdG1hcChhcy5tYXRyaXgoaGVhdG1hcF9tYXRyaXhbMToxMDAwLF0pLAogICAgICBzaG93X3Jvd19kZW5kID0gVFJVRSxzaG93X2NvbHVtbl9kZW5kID0gVFJVRSwKICAgICAgY29sPWhlYXRtYXBfY29sLHNob3dfY29sdW1uX25hbWVzID0gVFJVRSwKICAgICAgc2hvd19yb3dfbmFtZXMgPSBGQUxTRSxzaG93X2hlYXRtYXBfbGVnZW5kID0gVFJVRSwgaGVhdG1hcF9sZWdlbmRfcGFyYW0gPSBsaXN0KHRpdGxlID0gIiBIZWF0bWFwIHdpdGggcm93IG5vcm1hbGl6YXRpb24iKSkKYGBgCkZpZ3VyZSAyOiBIZWF0bWFwIGZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBvZiB0aGUgZmlyc3QgMTAwMCBnZW5lcywgb3JkZXJlZCBieSBlbnNlbWJsIGlkLCBhZnRlciByb3cgbm9ybWFsaXphdGlvbi4gT25seSB0aGUgZmlyc3QgMTAwMCBnZW5lcyBhcmUgc2hvd24gZHVlIHRvIHJlbmRlcmluZyByZXNvdXJjZSBpc3N1ZXMKW0BsNjsgQGhlYXRtYXA7IEBjb2xvcl0KCnlvdSBjYW4gc2VlIHRoYXQgdGhlIHNhbXBsZXMgYXJlIG1vcmUgc2ltaWxhciB0byBlYWNoIG90aGVyIGF0IHRoZSBzYW1lIHRpbWUgcG9pbnQgdGhhbiB3aGV0aGVyIG9yIG5vdCBpdCBpcyBjb250cm9sLCBJIHRoaW5rIHRoYXQgaXMgcmVhc29uYWJsZSBzaW5jZSBtYW55IGdlbmUgZ2V0IHNpbXVsYXRlZCBhbmQgcmVwcmVzc2VkIGR1cmluZyBkZXZlbG9wbWVudCwgc28gdGhlIGdlbmVzIHRoYXQgYXJlIG9uIGlzIGRpZmZlcmVudCBmcm9tIG9uZSBkZXZlbG9wbWVudCB0aW1lIHBvaW50LCB0byBhbm90aGVyLiAKCiMgSGVhdG1hcCBmb3IgdG9waGl0cyB1c2luZyBMaW1tYQoKV2Ugd2lsbCBiZSBmaXR0aW5nIHRoZSBkYXRhIHRvIHRoZSBhIGxpbmVhciBtb2RlbCBhbmQgdXNpbmcgZW1waXJpY2FsIGJheWVzIHRvIApjYWxjdWxhdGUgcC12YWx1ZXMuIFRoZW4gd2Ugd2lsbCB1c2luZyBCZW5qYW1uaS1Ib2NoYmVyZyBmb3IgbXVsdGlwbGUgaHlwb3RoZXNpcwp0ZXN0aW5nLCBmb3IgY29ycmVjdGluZyBvdXIgcC12YWx1ZXMuCgojIyBTaW1wbGUgTW9kZWwKClRoZSBzaW1wbGUgbW9kZWwgaXMgd2hlcmUgdGhlIG1vZGVsIGFjY291bnRzIGZvciB0aGUgY29uZGl0aW9uIChjb250cm9sIHZzIApleHBlcmltZW50KSwgYW5kIHRoZSBkYXkgdGhhdCB0aGUgZGF0YSB3YXMgY29sbGVjdGVkIG9uLCBidXQgbm90IGFjY291bnQgCmZvciB3aGljaCBvcmdhbm9pZHMgaXMgdGhlIGRhdGEgY29sbGVjdGVkIGZyb20uIAoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLmNhcD0nVGFibGUgMzogU2hvd2luZyBmb3IgZWNhaCBzYW1wbGUsIHdoaWNoIG9yZ2Fub2lkLCBhdCB3aGF0IHRpbWUsIGFuZCB3aGF0IGNvbmRpdGlvbiBpdCBpcyd9CnNhbXBsZXMgPC0gZGF0YS5mcmFtZShsYXBwbHkoY29sbmFtZXMoQ1JYX0V4cGVyaW1lbnRSYXdDb3VudEZpbHRlcmVkKVszOm5jb2woQ1JYX0V4cGVyaW1lbnRSYXdDb3VudEZpbHRlcmVkKV0sIEZVTj1mdW5jdGlvbih4KXt1bmxpc3Qoc3Ryc3BsaXQoeCwgc3BsaXQgPSAiXFxfIikpW2MoMSwgMiwgMyldfSkpCnNhbXBsZXMKY29sbmFtZXMoc2FtcGxlcykgPC0gY29sbmFtZXMoQ1JYX0V4cGVyaW1lbnRSYXdDb3VudEZpbHRlcmVkKVszOm5jb2woQ1JYX0V4cGVyaW1lbnRSYXdDb3VudEZpbHRlcmVkKV0Kcm93bmFtZXMoc2FtcGxlcykgPC0gYygiY29uZGl0aW9uIiwgInRpbWUiLCJwYXRpZW50IikKc2FtcGxlcyA8LSBkYXRhLmZyYW1lKHQoc2FtcGxlcykpCgojIG1ha2UgdGhlIG1vZGVsCm1vZGVsX2Rlc2lnbiA8LSBtb2RlbC5tYXRyaXgofiBzYW1wbGVzJGNvbmRpdGlvbitzYW1wbGVzJHRpbWUpCgojIHRoZW4gcHV0IGV2ZXJ5dGhpbmcgaW50byBhIG1hdHJpeApleHByZXNzaW9uTWF0cml4IDwtIGFzLm1hdHJpeChub3JtYWxpemVkX2NvdW50X2RhdGFbLDM6bmNvbChDUlhfRXhwZXJpbWVudFJhd0NvdW50RmlsdGVyZWQpXSkKcm93bmFtZXMoZXhwcmVzc2lvbk1hdHJpeCkgPC0KICBub3JtYWxpemVkX2NvdW50X2RhdGEkZW5zZW1ibF9nZW5lX2lkCmNvbG5hbWVzKGV4cHJlc3Npb25NYXRyaXgpIDwtCiAgY29sbmFtZXMobm9ybWFsaXplZF9jb3VudF9kYXRhKVszOm5jb2woQ1JYX0V4cGVyaW1lbnRSYXdDb3VudEZpbHRlcmVkKV0KbWluaW1hbFNldCA8LSBCaW9iYXNlOjpFeHByZXNzaW9uU2V0KGFzc2F5RGF0YT1leHByZXNzaW9uTWF0cml4KQoKIyBmaXQgdG8gbW9kZWwKZml0IDwtIGxpbW1hOjpsbUZpdChtaW5pbWFsU2V0LCBtb2RlbF9kZXNpZ24pCiMgdXNlIGVtcGlyaWNhbCBiYXllcyB0byBjYWxjdWxhdGUgcC12YWx1ZQpmaXQyIDwtIGxpbW1hOjplQmF5ZXMoZml0LHRyZW5kPVRSVUUpCgp0b3BmaXQgPC0gbGltbWE6OnRvcFRhYmxlKGZpdDIsCiAgICAgICAgICAgICAgICAgICBjb2VmPW5jb2wobW9kZWxfZGVzaWduKSwKICAgICAgICAgICAgICAgICAgIGFkanVzdC5tZXRob2QgPSAiQkgiLAogICAgICAgICAgICAgICAgICAgbnVtYmVyID0gbnJvdyhleHByZXNzaW9uTWF0cml4KSkKI21lcmdlIGhnbmMgbmFtZXMgdG8gdG9wZml0IHRhYmxlCm91dHB1dF9oaXRzIDwtIG1lcmdlKG5vcm1hbGl6ZWRfY291bnRfZGF0YVssMToyXSwKICAgICAgICAgICAgICAgICAgICAgdG9wZml0LAogICAgICAgICAgICAgICAgICAgICBieS55PTAsYnkueD0xLAogICAgICAgICAgICAgICAgICAgICBhbGwueT1UUlVFKQojc29ydCBieSBwdmFsdWUKb3V0cHV0X2hpdHMgPC0gb3V0cHV0X2hpdHNbb3JkZXIob3V0cHV0X2hpdHMkUC5WYWx1ZSksXQpgYGAKVGFibGUgMzogU2hvd2luZyBmb3IgZWNhaCBzYW1wbGUsIHdoaWNoIG9yZ2Fub2lkLCBhdCB3aGF0IHRpbWUsIGFuZCB3aGF0IGNvbmRpdGlvbiBpdCBpcwoKW0BsNjsgQGJpb2Jhc2U7IEBsaW1tYV0KCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcuY2FwPSdUYWJsZSA0OiBTaG93IHRoZSBsaW1tYSBvdXRwdXQgYWZ0ZXIgZml0dGluZyB0aGUgZGF0YSB0byB0aGUgc2ltcGxlIG1vZGVsLCBjYWxjdWxhdGluZyB0aGUgcC12YWx1ZSwgYW5kIGNvcnJlY3RpbmcgdGhlIHAtdmFsdWUuIFRoaXMgaXMgb25seSBzaG93aW5nIHRoZSBmaXJzdCAxMCwgb3JkZXJlZCBieSBwIHZhbHVlLiBOb3RlIHRoYXQgdGhlIHAgdmFsdWUgYW5kIGFkanVzdGVkIHAgdmFsdWVzIGFyZSBub3QgYWN0dWFsbHkgMCwgdGhleSBhcmUganVzdCByb3VuZGVkIHRvIDAnfQprbml0cjo6a2FibGUob3V0cHV0X2hpdHNbMToxMCwxOjddLHR5cGU9Imh0bWwiLHJvdy5uYW1lcyA9IEZBTFNFKQpgYGAKVGFibGUgNDogU2hvdyB0aGUgbGltbWEgb3V0cHV0IGFmdGVyIGZpdHRpbmcgdGhlIGRhdGEgdG8gdGhlIHNpbXBsZSBtb2RlbCwgY2FsY3VsYXRpbmcgdGhlIHAtdmFsdWUsIGFuZCBjb3JyZWN0aW5nIHRoZSBwLXZhbHVlLiBUaGlzIGlzIG9ubHkgc2hvd2luZyB0aGUgZmlyc3QgMTAsIG9yZGVyZWQgYnkgcCB2YWx1ZS4gTm90ZSB0aGF0IHRoZSBwIHZhbHVlIGFuZCBhZGp1c3RlZCBwIHZhbHVlcyBhcmUgbm90IGFjdHVhbGx5IDAsIHRoZXkgYXJlIGp1c3Qgcm91bmRlZCB0byAwCgpbQGw2OyBAa25pdHJdCgpUaGlzIGlzIGhvdyBtYW55IGdlbmVzIHRoYXQgaGF2ZSBwdmFsdWUgdW5kZXIgMC4wNQoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGVuZ3RoKHdoaWNoKG91dHB1dF9oaXRzJFAuVmFsdWUgPCAwLjA1KSkKYGBgCltAbDZdCgoKVGhpcyBpcyBob3cgbWFueSBnZW5lcyB0aGF0IHN0aWxsIHB2YWx1ZSBvZiBsZXNzIHRoYW4gMC4wNSBhZnRlciBhZGp1c3RtZW50CmBgYHtyIHdhcm5pbmc9RkFMU0V9Cmxlbmd0aCh3aGljaChvdXRwdXRfaGl0cyRhZGouUC5WYWwgPCAwLjA1KSkKYGBgCltAbDZdCgoKIyMgT3JnYW5vaWQgTW9kZWwKClRoZSBvcmdhbm9pZCBtb2RlbCBpcyB3aGVyZSB0aGUgbW9kZWwgYWNjb3VudHMgZm9yIHRoZSBjb25kaXRpb24gKGNvbnRyb2wgdnMgCmV4cGVyaW1lbnQpLCBhbmQgdGhlIGRheSB0aGF0IHRoZSBkYXRhIHdhcyBjb2xsZWN0ZWQgb24sIGFuZCAKZm9yIHdoaWNoIG9yZ2Fub2lkcyBpcyB0aGUgZGF0YSBjb2xsZWN0ZWQgZnJvbS4gCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIG1ha2Ugb3JnYW5vaWRzIG1vZGVsLAptb2RlbF9kZXNpZ25fb3JnIDwtIG1vZGVsLm1hdHJpeCgKICB+IHNhbXBsZXMkcGF0aWVudCArIHNhbXBsZXMkY29uZGl0aW9uICsgc2FtcGxlcyR0aW1lKQpgYGAKW0BsNjsgQGtuaXRyXQoKQ2hhbmdlIHRoZSBtb2RlbCwgZml0IHRoZSBkYXRhIHRvIHRoZSBtb2RlbCwgdGhlbiBhcHBseSBlbXBpcmNhbCBiYXllcyAsIAp0aGVuIGNvcnJlY3Qgd2l0aCBCSAoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyBmaXQgdG8gbW9kZWwKZml0X29yZyA8LSBsaW1tYTo6bG1GaXQobWluaW1hbFNldCwgbW9kZWxfZGVzaWduX29yZykKIyB1c2UgZWJheWVzIHRvIGNhbGN1bGF0ZSBwLXZhbHVlCmZpdDJfb3JnIDwtIGxpbW1hOjplQmF5ZXMoZml0X29yZyx0cmVuZD1UUlVFKQoKdG9wZml0X29yZyA8LSBsaW1tYTo6dG9wVGFibGUoZml0Ml9vcmcsCiAgICAgICAgICAgICAgICAgICBjb2VmPW5jb2wobW9kZWxfZGVzaWduX29yZyksCiAgICAgICAgICAgICAgICAgICBhZGp1c3QubWV0aG9kID0gIkJIIiwKICAgICAgICAgICAgICAgICAgIG51bWJlciA9IG5yb3coZXhwcmVzc2lvbk1hdHJpeCkpCgojbWVyZ2UgaGduYyBuYW1lcyB0byB0b3BmaXQgdGFibGUKb3V0cHV0X2hpdHNfb3JnIDwtIG1lcmdlKG5vcm1hbGl6ZWRfY291bnRfZGF0YVssMToyXSwKICAgICAgICAgICAgICAgICAgICAgICAgIHRvcGZpdF9vcmcsIGJ5Lnk9MCwgYnkueD0xLCBhbGwueT1UUlVFKQojc29ydCBieSBwdmFsdWUKb3V0cHV0X2hpdHNfb3JnIDwtIG91dHB1dF9oaXRzX29yZ1tvcmRlcihvdXRwdXRfaGl0c19vcmckUC5WYWx1ZSksXQoKYGBgCltAbDY7IEBsaW1tYV0KCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcuY2FwPSdUYWJsZSA1OiBTaG93IHRoZSBsaW1tYSBvdXRwdXQgYWZ0ZXIgZml0dGluZyB0aGUgZGF0YSB0byB0aGUgb3JnYW5vaWQgbW9kZWwsIGNhbGN1bGF0aW5nIHRoZSBwLXZhbHVlLCBhbmQgY29ycmVjdGluZyB0aGUgcC12YWx1ZS4gVGhpcyBpcyBvbmx5IHNob3dpbmcgdGhlIGZpcnN0IDEwLCBvcmRlcmVkIGJ5IHAtdmFsdWUuIE5vdGUgdGhhdCB0aGUgcCB2YWx1ZSBhbmQgYWRqdXN0ZWQgcCB2YWx1ZXMgYXJlIG5vdCBhY3R1YWxseSAwLCB0aGV5IGFyZSBqdXN0IHJvdW5kZWQgdG8gMCd9CmtuaXRyOjprYWJsZShvdXRwdXRfaGl0c19vcmdbMToxMCwxOjddLHR5cGU9Imh0bWwiLHJvdy5uYW1lcyA9IEZBTFNFKQpgYGAKVGFibGUgNTogU2hvdyB0aGUgbGltbWEgb3V0cHV0IGFmdGVyIGZpdHRpbmcgdGhlIGRhdGEgdG8gdGhlIG9yZ2Fub2lkIG1vZGVsLCBjYWxjdWxhdGluZyB0aGUgcC12YWx1ZSwgYW5kIGNvcnJlY3RpbmcgdGhlIHAtdmFsdWUuIFRoaXMgaXMgb25seSBzaG93aW5nIHRoZSBmaXJzdCAxMCwgb3JkZXJlZCBieSBwLXZhbHVlLiBOb3RlIHRoYXQgdGhlIHAgdmFsdWUgYW5kIGFkanVzdGVkIHAgdmFsdWVzIGFyZSBub3QgYWN0dWFsbHkgMCwgdGhleSBhcmUganVzdCByb3VuZGVkIHRvIDAKCltAbDY7IEBrbml0cl0KCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsZW5ndGgod2hpY2gob3V0cHV0X2hpdHNfb3JnJFAuVmFsdWUgPCAwLjA1KSkKYGBgCltAbDZdCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsZW5ndGgod2hpY2gob3V0cHV0X2hpdHNfb3JnJGFkai5QLlZhbCA8IDAuMDUpKQpgYGAKW0BsNl0KCiMjIENvbXBhcmluZyBib3RoIG1vZGVscwoKVGhlcmUgaXMgYWN0dWFsbHkgbGVzcywgYnV0IGxldCB1cyBsb29rIGF0IGl0IGZyb20gYSBncmFwaAoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLmNhcD0nRmlndXJlIDM6IFNob3dpbmcgZWFjaCBnZW5lIG9uIGEgZ3JhcGggd2hlcmUgdGhlIGNvb3JkaW5hdGVzIGlzIHRoZSBjb3JyZWN0ZWQgcC12YWx1ZXMgdXNpbmcgdGhlIHNpbXBsZSBtb2RlbCBhbmQgdGhlIG9yZ2Fub2lkIG1vZGVsIGluIGxpbW1hLCB6b29tZWQgaW4gb24gMCB0byAwLjEuIEhpZ2hsaWdodGluZyBzaWduaWZpY2FudCBnZW5lcyBpbiBib3RoIG1vZGVsIGFzIHJlZCwgaW4gc2ltcGxlIG1vZGVsIG9ubHkgYXMgb3JhbmdlLCBpbiBvcmdhbm9pZCBtb2RlbCBvbmx5IGFzIHJlZCwgYW5kIG5vbi1zaWduaWZpY2FudCBnZW5lcyBhcyBibGFjayd9CgojIGNvbXBhcmUgZWFjaCBnZW5lcyBmcm9tIHRoZSBhZGp1c3RlZCBwdmFsdWUgZnJvbSBzaW1wbGUgbW9kZWwgdG8gdGhlCiMgYWRqdXN0ZWQgdmFsdWUgZnJvbSBvcmdhbm9pZCBtb2RlbAoKc2ltcGxlX21vZGVsX3B2YWx1ZXMgPC0gZGF0YS5mcmFtZShlbnNlbWJsX2lkID0KICBvdXRwdXRfaGl0cyRlbnNlbWJsX2dlbmVfaWQsCiAgc2ltcGxlX3B2YWx1ZT1vdXRwdXRfaGl0cyRhZGouUC5WYWwpCgpvcmdfbW9kZWxfcHZhbHVlcyA8LSAgZGF0YS5mcmFtZShlbnNlbWJsX2lkID0KICBvdXRwdXRfaGl0c19vcmckZW5zZW1ibF9nZW5lX2lkLAogIG9yZ2Fub2lkX3B2YWx1ZSA9IG91dHB1dF9oaXRzX29yZyRhZGouUC5WYWwpCgp0d29fbW9kZWxzX3B2YWx1ZXMgPC0gbWVyZ2Uoc2ltcGxlX21vZGVsX3B2YWx1ZXMsCiAgb3JnX21vZGVsX3B2YWx1ZXMsYnkueD0xLGJ5Lnk9MSkKCiMgc2lnbmlmaWNhbnQgc2ltcGxlIG1vZGVsIGlzIG9yYW5nZSwgc2lnbmlmaWNhbnQgb3JnYW5vaWQgbW9kZWwgaXMgYmx1ZSwgCiMgc2lnbmlmaWNhbnQgaW4gYm90aCBpcyByZWQsIGV2ZXJ5dGhpbmcgZWxzZSBpcyBibGFjawp0d29fbW9kZWxzX3B2YWx1ZXMkY29sb3VyIDwtICJibGFjayIKCnR3b19tb2RlbHNfcHZhbHVlcyRjb2xvdXJbCiAgdHdvX21vZGVsc19wdmFsdWVzJHNpbXBsZV9wdmFsdWU8MC4wNV0gPC0gIm9yYW5nZSIKCnR3b19tb2RlbHNfcHZhbHVlcyRjb2xvdXJbCiAgdHdvX21vZGVsc19wdmFsdWVzJG9yZ2Fub2lkX3B2YWx1ZTwwLjA1XSA8LSAiYmx1ZSIKCnR3b19tb2RlbHNfcHZhbHVlcyRjb2xvdXJbCiAgdHdvX21vZGVsc19wdmFsdWVzJHNpbXBsZV9wdmFsdWU8MC4wNSAmCiAgdHdvX21vZGVsc19wdmFsdWVzJG9yZ2Fub2lkX3B2YWx1ZTwwLjA1XSA8LSAicmVkIgoKcGxvdCh0d29fbW9kZWxzX3B2YWx1ZXMkc2ltcGxlX3B2YWx1ZSwKICAgICB0d29fbW9kZWxzX3B2YWx1ZXMkb3JnYW5vaWRfcHZhbHVlLAogICAgIGNvbCA9IHR3b19tb2RlbHNfcHZhbHVlcyRjb2xvdXIsCiAgICAgeGxhYiA9ICJTaW1wbGUgbW9kZWwgYWRqdXN0ZWQgcC12YWx1ZXMiLAogICAgIHlsYWIgPSJPcmdhbm9pZCBtb2RlbCBhZGp1c3RlZCBwLXZhbHVlcyIsCiAgICAgbWFpbj0iU2ltcGxlIHZzIE9yZ2Fub2lkIExpbW1hIiwKICAgICB4bGltID0gYygwLCAwLjEpLAogICAgIHlsaW0gPSBjKDAsIDAuMSkpCmBgYApGaWd1cmUgMzogU2hvd2luZyBlYWNoIGdlbmUgb24gYSBncmFwaCB3aGVyZSB0aGUgY29vcmRpbmF0ZXMgaXMgdGhlIGNvcnJlY3RlZCBwLXZhbHVlcyB1c2luZyB0aGUgc2ltcGxlIG1vZGVsIGFuZCB0aGUgb3JnYW5vaWQgbW9kZWwgaW4gbGltbWEsIHpvb21lZCBpbiBvbiAwIHRvIDAuMS4gSGlnaGxpZ2h0aW5nIHNpZ25pZmljYW50IGdlbmVzIGluIGJvdGggbW9kZWwgYXMgcmVkLCBpbiBzaW1wbGUgbW9kZWwgb25seSBhcyBvcmFuZ2UsIGluIG9yZ2Fub2lkIG1vZGVsIG9ubHkgYXMgcmVkLCBhbmQgbm9uLXNpZ25pZmljYW50IGdlbmVzIGFzIGJsYWNrCltAbDZdCgpUbyBkZXRlcm1pbmUgd2hpY2ggbW9kZWwgaXMgbW9yZSBhcHByb3ByaWF0ZSwgd2UgdGFrZSBhIGxvb2sgYXQgcC12YWx1ZSBvZiB0aGUKZ2VuZSBvZiBpbnRlcmVzdCwgQ1JYLiAKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy5jYXA9J0ZpZ3VyZSA0OiBTaG93aW5nIGVhY2ggZ2VuZSBvbiBhIGdyYXBoIHdoZXJlIHRoZSBjb29yZGluYXRlcyBpcyB0aGUgY29ycmVjdGVkIHAtdmFsdWVzIHVzaW5nIHRoZSBzaW1wbGUgbW9kZWwgYW5kIHRoZSBvcmdhbm9pZCBtb2RlbCBpbiBsaW1tYSwgem9vbWVkIGluIG9uIDAgdG8gMC4xLiBIaWdobGlnaHRpbmcgb25seSB0aGUgZ2VuZSBvZiBpbnRlcmVzdCwgQ1JYIGFzIHJlZCd9CgojIHNhbWUgdGhpbmcgYXMgYWJvdmUsIGJ1dCBsb29raW5nIGF0IHpvb21lZCBpbiB0byBsb29rIGF0IHRoZSBnZW5lIG9mIGludGVyZXN0CgplbnNlbWJsX29mX2ludGVyZXN0IDwtIG5vcm1hbGl6ZWRfY291bnRfZGF0YSRlbnNlbWJsX2dlbmVfaWRbCiAgd2hpY2gobm9ybWFsaXplZF9jb3VudF9kYXRhJGhnbmNfc3ltYm9sID09ICJDUlgiKV0KdHdvX21vZGVsc19wdmFsdWVzJGNvbG91ciA8LSAiZ3JleSIKdHdvX21vZGVsc19wdmFsdWVzJGNvbG91clt0d29fbW9kZWxzX3B2YWx1ZXMkZW5zZW1ibF9pZD09CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbnNlbWJsX29mX2ludGVyZXN0XSA8LSAicmVkIgpwbG90KHR3b19tb2RlbHNfcHZhbHVlcyRzaW1wbGVfcHZhbHVlLAogICAgIHR3b19tb2RlbHNfcHZhbHVlcyRvcmdhbm9pZF9wdmFsdWUsCiAgICAgY29sID0gdHdvX21vZGVsc19wdmFsdWVzJGNvbG91ciwKICAgICB4bGFiID0gInNpbXBsZSBtb2RlbCBhZGp1c3RlZCBwLXZhbHVlcyIsCiAgICAgeWxhYiA9Im9yZ2Fub2lkIG1vZGVsIGFkanVzdGVkICBwLXZhbHVlcyIsCiAgICAgbWFpbj0iU2ltcGxlIHZzIE9yZ2Fub2lkIExpbW1hIiwKICAgICB4bGltID0gYygwLCAwLjEpLAogICAgIHlsaW0gPSBjKDAsIDAuMSkpCnBvaW50cyh0d29fbW9kZWxzX3B2YWx1ZXNbd2hpY2goCiAgdHdvX21vZGVsc19wdmFsdWVzJGVuc2VtYmxfaWQgPT0gZW5zZW1ibF9vZl9pbnRlcmVzdCksMjozXSwKICAgICAgIHBjaD0yMCwgY29sPSJyZWQiLCBjZXg9MS41KQpsZWdlbmQoMCwxLGxlZ2VuZD1jKCJDUlgiLCJyZXN0IiksCiAgICAgICBmaWxsPWMoInJlZCIsImdyZXkiKSxjZXggPSAwLjcpCmBgYApGaWd1cmUgNDogU2hvd2luZyBlYWNoIGdlbmUgb24gYSBncmFwaCB3aGVyZSB0aGUgY29vcmRpbmF0ZXMgaXMgdGhlIGNvcnJlY3RlZCBwLXZhbHVlcyB1c2luZyB0aGUgc2ltcGxlIG1vZGVsIGFuZCB0aGUgb3JnYW5vaWQgbW9kZWwgaW4gbGltbWEsIHpvb21lZCBpbiBvbiAwIHRvIDAuMS4gSGlnaGxpZ2h0aW5nIG9ubHkgdGhlIGdlbmUgb2YgaW50ZXJlc3QsIENSWCBhcyByZWQKW0BsNl0KClRoZSBzaW1wbGUgbW9kZWwgaXMgYmV0dGVyLCBJIGJlbGlldmUgdGhhdCBpcyB0aGUgYmVjYXVzZSB0aGVyZSBhcmUgbWluaW1hbCAKZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSByZXRpbmFsIG9yZ2Fub2lkcywgc2luY2UgdGhleSBhcmUgZGVyaXZlZCBmcm9tIGlQU0MgCnRoYXQgaXMgZnJvbSB0aGUgc2FtZSBwYXRpZW50LiAKCkFzIHdlbGwsIHRoZXJlIGlzIGEgc2lnbmlmaWNhbnQgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSBsb2cgRkMKCgpgYGB7cn0Kb3V0cHV0X2hpdHMkbG9nRkNbb3V0cHV0X2hpdHMkZW5zZW1ibF9nZW5lX2lkID09IGVuc2VtYmxfb2ZfaW50ZXJlc3RdCmBgYAoKYGBge3J9Cm91dHB1dF9oaXRzX29yZyRsb2dGQ1tvdXRwdXRfaGl0cyRlbnNlbWJsX2dlbmVfaWQgPT0gZW5zZW1ibF9vZl9pbnRlcmVzdF0KYGBgCgoKCiMjIEhlYXRtYXAgZnJvbSBsaW1tYSAKCkxldCB1cyBsb29rIGF0IHRoZSBoZWF0bWFwIG9mIG9ubHkgdGhlIHRvcCBoaXRzIChieSBjb3JyZWN0ZWQgcHZhbHVlKSwKaW4gdGhlIHNpbXBsZSBtb2RlbAoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLmNhcD0nRmlndXJlIDU6IEhlYXRtYXAgZm9yIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIG9mIHRoZSBmaXJzdCAxMDAwIGdlbmVzLCBvcmRlcmVkIGJ5IGVuc2VtYmwgaWQsIGFmdGVyIHJvdyBub3JtYWxpemF0aW9uIGZvciBzaWduaWZpY2FudCBnZW5lcyBieSBjb3JyZWN0ZWQgcC12YWx1ZSB1c2luZyB0aGUgc2ltcGxlIG1vZGVsIGluIGxpbW1hLiBPbmx5IHRoZSBmaXJzdCAxMDAwIGdlbmVzIGFyZSBzaG93biBkdWUgdG8gcmVuZGVyaW5nIHJlc291cmNlIGlzc3Vlcyd9CnRvcF9oaXRzIDwtIG91dHB1dF9oaXRzJGVuc2VtYmxfZ2VuZV9pZFsKICBvdXRwdXRfaGl0cyRhZGouUC5WYWw8MC4wNV0KCiMgb25seSBwaWNraW5nIHRvcCBoaXRzCmhlYXRtYXBfbWF0cml4X3RvcGhpdHMgPC0gdCgKICBzY2FsZSh0KGhlYXRtYXBfbWF0cml4Wwp3aGljaChyb3duYW1lcyhoZWF0bWFwX21hdHJpeCkgJWluJSB0b3BfaGl0cyksXSkpKQoKIyBzYW1lIGNvbG9yIHNldHRpbmcgYXMgYWJvdmUKaWYobWluKGhlYXRtYXBfbWF0cml4X3RvcGhpdHMpID09IDApewpoZWF0bWFwX2NvbCA9IGNvbG9yUmFtcDIoYyggMCwgbWF4KGhlYXRtYXBfYXRyaXhfdG9waGl0cykpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMoICJ3aGl0ZSIsICJyZWQiKSkKfSBlbHNlIHsKaGVhdG1hcF9jb2wgPSBjb2xvclJhbXAyKGMobWluKGhlYXRtYXBfbWF0cml4X3RvcGhpdHMpLCAwLAogICAgICBtYXgoaGVhdG1hcF9tYXRyaXhfdG9waGl0cykpLCBjKCJibHVlIiwgIndoaXRlIiwgInJlZCIpKQogIH0KCkhlYXRtYXAoYXMubWF0cml4KGhlYXRtYXBfbWF0cml4X3RvcGhpdHNbMToxMDAwLF0pLAogICAgICAgICAgICAgICAgICAgICAgICAgICBjbHVzdGVyX3Jvd3MgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICBjbHVzdGVyX2NvbHVtbnMgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19yb3dfZGVuZCA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaG93X2NvbHVtbl9kZW5kID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbD1oZWF0bWFwX2NvbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfY29sdW1uX25hbWVzID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfcm93X25hbWVzID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaG93X2hlYXRtYXBfbGVnZW5kID0gVFJVRSwgCiAgICAgICAgaGVhdG1hcF9sZWdlbmRfcGFyYW0gPSBsaXN0KHRpdGxlID0gIkhlYXRtYXAgZnJvbSBMaW1tYSIpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCgpgYGAKRmlndXJlIDU6IEhlYXRtYXAgZm9yIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIG9mIHRoZSBmaXJzdCAxMDAwIGdlbmVzLCBvcmRlcmVkIGJ5IGVuc2VtYmwgaWQsIGFmdGVyIHJvdyBub3JtYWxpemF0aW9uIGZvciBzaWduaWZpY2FudCBnZW5lcyBieSBjb3JyZWN0ZWQgcC12YWx1ZSB1c2luZyB0aGUgc2ltcGxlIG1vZGVsIGluIGxpbW1hLiBPbmx5IHRoZSBmaXJzdCAxMDAwIGdlbmVzIGFyZSBzaG93biBkdWUgdG8gcmVuZGVyaW5nIHJlc291cmNlIGlzc3VlcwpbQGw2XQoKCkxldCB1cyBub3QgY2x1c3RlciB0aGUgZ2VuZXMgYnV0IGdyb3VwIGNvbnRyb2xzIGFuZCBleHBlcmltZW50cyB0b2dldGhlcgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLmNhcD0nRmlndXJlIDY6IEhlYXRtYXAgZm9yIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIG9mIHRoZSBmaXJzdCAxMDAwIGdlbmVzLCBvcmRlcmVkIGJ5IGVuc2VtYmwgaWQsIGFmdGVyIHJvdyBub3JtYWxpemF0aW9uIGZvciBzaWduaWZpY2FudCBnZW5lcyBiYXNlZCBvbiBjb3JyZWN0ZWQgcC12YWx1ZSB1c2luZyB0aGUgc2ltcGxlIG1vZGVsIGluIGxpbW1hLCB3aXRob3V0IGNsdXN0ZXJpbmcsIGFuZCBncm91cCBjb250cm9sIGFuZCBleHBlcmltZW50IHRvZ2V0aGVyLCByZXNwZWN0aXZlbHkuIE9ubHkgdGhlIGZpcnN0IDEwMDAgZ2VuZXMgYXJlIHNob3duIGR1ZSB0byByZW5kZXJpbmcgcmVzb3VyY2UgaXNzdWVzJ30KdG9wX2hpdHMgPC0gb3V0cHV0X2hpdHMkZW5zZW1ibF9nZW5lX2lkWwogIG91dHB1dF9oaXRzJGFkai5QLlZhbDwwLjA1XQoKIyBvbmx5IHBpY2tpbmcgdG9wIGhpdHMKaGVhdG1hcF9tYXRyaXhfdG9waGl0cyA8LSB0KAogIHNjYWxlKHQoaGVhdG1hcF9tYXRyaXhbCndoaWNoKHJvd25hbWVzKGhlYXRtYXBfbWF0cml4KSAlaW4lIHRvcF9oaXRzKSxdKSkpCgpoZWF0bWFwX21hdHJpeF90b3BoaXRzIDwtIGhlYXRtYXBfbWF0cml4X3RvcGhpdHNbLApjKAogIGdyZXAoY29sbmFtZXMoaGVhdG1hcF9tYXRyaXhfdG9waGl0cyksIHBhdHRlcm4gPSAiXFxDUlhMQ0EiKSwKICBncmVwKGNvbG5hbWVzKGhlYXRtYXBfbWF0cml4X3RvcGhpdHMpLCBwYXR0ZXJuID0gIlxcQ29udHJvbCIpCiApCl0KCiMgc2FtZSBjb2xvciBzZXR0aW5nIGFzIGFib3ZlCmlmKG1pbihoZWF0bWFwX21hdHJpeF90b3BoaXRzKSA9PSAwKXsKaGVhdG1hcF9jb2wgPSBjb2xvclJhbXAyKGMoIDAsIG1heChoZWF0bWFwX21hdHJpeF90b3BoaXRzKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYyggIndoaXRlIiwgInJlZCIpKQp9IGVsc2UgewpoZWF0bWFwX2NvbCA9IGNvbG9yUmFtcDIoYyhtaW4oaGVhdG1hcF9tYXRyaXhfdG9waGl0cyksIDAsCiAgICAgICAgICAgICAgICAgICAgbWF4KGhlYXRtYXBfbWF0cml4X3RvcGhpdHMpKSwKICAgICAgICAgICAgICAgICAgICBjKCJibHVlIiwgIndoaXRlIiwgInJlZCIpKQp9CgpIZWF0bWFwKGFzLm1hdHJpeChoZWF0bWFwX21hdHJpeF90b3BoaXRzWzE6MTAwMCxdKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9yb3dzID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9jb2x1bW5zID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaG93X3Jvd19kZW5kID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfY29sdW1uX2RlbmQgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sPWhlYXRtYXBfY29sLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19jb2x1bW5fbmFtZXMgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19yb3dfbmFtZXMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfaGVhdG1hcF9sZWdlbmQgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGVhdG1hcF9sZWdlbmRfcGFyYW0gPSBsaXN0KHRpdGxlID0gIkxpbW1hIEhlYXRtYXAgd2l0aG91dCBjbHVzdGVyaW5nIikpCmBgYApGaWd1cmUgNjogSGVhdG1hcCBmb3IgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gb2YgdGhlIGZpcnN0IDEwMDAgZ2VuZXMsIG9yZGVyZWQgYnkgZW5zZW1ibCBpZCwgYWZ0ZXIgcm93IG5vcm1hbGl6YXRpb24gZm9yIHNpZ25pZmljYW50IGdlbmVzIGJhc2VkIG9uIGNvcnJlY3RlZCBwLXZhbHVlIHVzaW5nIHRoZSBzaW1wbGUgbW9kZWwgaW4gbGltbWEsIHdpdGhvdXQgY2x1c3RlcmluZywgYW5kIGdyb3VwIGNvbnRyb2wgYW5kIGV4cGVyaW1lbnQgdG9nZXRoZXIsIHJlc3BlY3RpdmVseS4gT25seSB0aGUgZmlyc3QgMTAwMCBnZW5lcyBhcmUgc2hvd24gZHVlIHRvIHJlbmRlcmluZyByZXNvdXJjZSBpc3N1ZXMKW0BsNl0KCldlIGNhbiBjbGVhcmx5IHRlbGwgdGhhdCB0aGUgdGVzdCBjb25kaXRpb24gZG8gbm90IGxvb2sgbGlrZSBlYWNoIG90aGVyLgpUaGUgY29udHJvbHMgYWxzbyBkbyBub3QgbG9vayBsaWtlIGVhY2ggb3RoZXIuCgoKVGhlbiBhZ2FpbiBsb29rIGF0IHRvcCBoaXRzIHdpdGggbGVzcyB0aGFuIDAuMDEgcHZhbHVlCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy5jYXA9J0ZpZ3VyZSA3OiBIZWF0bWFwIGZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBvZiB0aGUgZmlyc3QgMTAwMCBnZW5lcywgb3JkZXJlZCBieSBlbnNlbWJsIGlkLCBhZnRlciByb3cgbm9ybWFsaXphdGlvbiBmb3IgaGl0cyB3aXRoIGNvcnJlY3RlZCBwLXZhbHVlIHNtYWxsZXIgdGhhbiAwLjAxIHVzaW5nIHRoZSBzaW1wbGUgbW9kZWwgaW4gbGltbWEsIHdpdGhvdXQgY2x1c3RlcmluZywgYW5kIGdyb3VwIGNvbnRyb2wgYW5kIGV4cGVyaW1lbnQgdG9nZXRoZXIsIHJlc3BlY3RpdmVseS4gT25seSB0aGUgZmlyc3QgMTAwMCBnZW5lcyBhcmUgc2hvd24gZHVlIHRvIHJlbmRlcmluZyByZXNvdXJjZSBpc3N1ZXMnfQoKIyBvbmx5IHRvcCBoaXRzIChsZXNzIHRoYW4gMC4wMSkKdG9wX2hpdHMgPC0gb3V0cHV0X2hpdHMkZW5zZW1ibF9nZW5lX2lkWwogIG91dHB1dF9oaXRzJGFkai5QLlZhbCA8IDAuMDFdCgpoZWF0bWFwX21hdHJpeF90b3BoaXRzIDwtIHQoCnNjYWxlKHQoaGVhdG1hcF9tYXRyaXhbd2hpY2gocm93bmFtZXMoaGVhdG1hcF9tYXRyaXgpICVpbiUgdG9wX2hpdHMpLF0pKSkKCiMgc2VwZXJhdGUgYmFzZWQgb24gY29udHJvbCB2cyB0ZXN0CmhlYXRtYXBfbWF0cml4X3RvcGhpdHMgPC0gaGVhdG1hcF9tYXRyaXhfdG9waGl0c1ssCiAgICAgICBjKGdyZXAoY29sbmFtZXMoaGVhdG1hcF9tYXRyaXhfdG9waGl0cykscGF0dGVybiA9ICJcXENSWExDQSIpLAogICAgICAgICBncmVwKGNvbG5hbWVzKGhlYXRtYXBfbWF0cml4X3RvcGhpdHMpLHBhdHRlcm4gPSAiXFxDb250cm9sIikpXQoKbnJvdyhoZWF0bWFwX21hdHJpeF90b3BoaXRzKQoKIyBzYW1lIGNvbG9yIHNldHRpbmcgYXMgYWJvdmUKaWYobWluKGhlYXRtYXBfbWF0cml4X3RvcGhpdHMpID09IDApewpoZWF0bWFwX2NvbCA9IGNvbG9yUmFtcDIoYyggMCwgbWF4KGhlYXRtYXBfbWF0cml4X3RvcGhpdHMpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjKCAid2hpdGUiLCAicmVkIikpCn0gZWxzZSB7CmhlYXRtYXBfY29sID0gY29sb3JSYW1wMihjKG1pbihoZWF0bWFwX21hdHJpeF90b3BoaXRzKSwgMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWF4KGhlYXRtYXBfbWF0cml4X3RvcGhpdHMpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjKCJibHVlIiwgIndoaXRlIiwgInJlZCIpKQp9CkhlYXRtYXAoYXMubWF0cml4KGhlYXRtYXBfbWF0cml4X3RvcGhpdHNbMToxMDAwLF0pLAogICAgICAgICAgICAgICAgY2x1c3Rlcl9yb3dzID0gVFJVRSwgIHNob3dfcm93X2RlbmQgPSBUUlVFLAogICAgICAgICAgICAgICAgY2x1c3Rlcl9jb2x1bW5zID0gRkFMU0Usc2hvd19jb2x1bW5fZGVuZCA9IEZBTFNFLAogICAgICAgICAgICAgICAgY29sPWhlYXRtYXBfY29sLHNob3dfY29sdW1uX25hbWVzID0gVFJVRSwKICAgICAgICAgICAgICAgIHNob3dfcm93X25hbWVzID0gRkFMU0Usc2hvd19oZWF0bWFwX2xlZ2VuZCA9IFRSVUUsIGhlYXRtYXBfbGVnZW5kX3BhcmFtID0gbGlzdCh0aXRsZSA9ICJMaW1tYSBIZWF0bWFwLCBubyBjbHVzdGVyLCAwLjEiKSkKYGBgCkZpZ3VyZSA3OiBIZWF0bWFwIGZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBvZiB0aGUgZmlyc3QgMTAwMCBnZW5lcywgb3JkZXJlZCBieSBlbnNlbWJsIGlkLCBhZnRlciByb3cgbm9ybWFsaXphdGlvbiBmb3IgaGl0cyB3aXRoIGNvcnJlY3RlZCBwLXZhbHVlIHNtYWxsZXIgdGhhbiAwLjAxIHVzaW5nIHRoZSBzaW1wbGUgbW9kZWwgaW4gbGltbWEsIHdpdGhvdXQgY2x1c3RlcmluZywgYW5kIGdyb3VwIGNvbnRyb2wgYW5kIGV4cGVyaW1lbnQgdG9nZXRoZXIsIHJlc3BlY3RpdmVseS4gT25seSB0aGUgZmlyc3QgMTAwMCBnZW5lcyBhcmUgc2hvd24gZHVlIHRvIHJlbmRlcmluZyByZXNvdXJjZSBpc3N1ZXMKW0BsNjsgQGhlYXRtYXA7IEBjb2xvcl0KCgojIEhlYXRtYXAgd2l0aCB0b3BoaXRzIGZyb20gZWRnZVIKCiMjIFNpbXBsZSBNb2RlbAoKVGhlIHNpbXBsZSBtb2RlbCBpcyB3aGVyZSB0aGUgbW9kZWwgYWNjb3VudHMgZm9yIHRoZSBjb25kaXRpb24gKGNvbnRyb2wgdnMgCmV4cGVyaW1lbnQpLCBhbmQgdGhlIGRheSB0aGF0IHRoZSBkYXRhIHdhcyBjb2xsZWN0ZWQgb24sIGJ1dCBub3QgYWNjb3VudCAKZm9yIHdoaWNoIG9yZ2Fub2lkcyBpcyB0aGUgZGF0YSBjb2xsZWN0ZWQgZnJvbS4gCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpmaWx0ZXJlZF9kYXRhX21hdHJpeCA8LSBhcy5tYXRyaXgoQ1JYX0V4cGVyaW1lbnRSYXdDb3VudEZpbHRlcmVkWywzOjI4XSkKcm93bmFtZXMoZmlsdGVyZWRfZGF0YV9tYXRyaXgpIDwtIENSWF9FeHBlcmltZW50UmF3Q291bnRGaWx0ZXJlZCRlbnMuaWQKZCA8LSBlZGdlUjo6REdFTGlzdChjb3VudHM9ZmlsdGVyZWRfZGF0YV9tYXRyaXgsIGdyb3VwPXNhbXBsZXMkY29uZGl0aW9uKQpkIDwtIGVkZ2VSOjplc3RpbWF0ZURpc3AoZCwgbW9kZWxfZGVzaWduKQpmaXQgPC0gZWRnZVI6OmdsbVFMRml0KGQsIG1vZGVsX2Rlc2lnbikKCmBgYApbQGw2OyBAZWRnZXJdCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcuY2FwPSdUYWJsZSA2OiBTaG93IHRoZSBvdXRwdXQgb2YgZ2xtUUxGaXQgYWZ0ZXIgZml0dGluZyB0aGUgZGF0YSB0byB0aGUgc2ltcGxlIG1vZGVsLCBjYWxjdWxhdGluZyB0aGUgcC12YWx1ZSwgYW5kIGNvcnJlY3RpbmcgdGhlIHAtdmFsdWUuIFRoaXMgaXMgb25seSBzaG93aW5nIHRoZSBmaXJzdCAxMCwgb3JkZXJlZCBieSBwLXZhbHVlLiBOb3RlIHRoYXQgdGhlIHAgdmFsdWUgYW5kIEZEUiBhcmUgbm90IGFjdHVhbGx5IDAsIHRoZXkgYXJlIGp1c3Qgcm91bmRlZCB0byAwJ30KcWxmLnBvc192c19uZWcgPC0gZWRnZVI6OmdsbVFMRlRlc3QoZml0LCBjb2VmPSdzYW1wbGVzJGNvbmRpdGlvbkNSWExDQScpCmtuaXRyOjprYWJsZShlZGdlUjo6dG9wVGFncyhxbGYucG9zX3ZzX25lZyksIHR5cGU9Imh0bWwiLHJvdy5uYW1lcyA9IEZBTFNFKQpgYGAKCgpbQGw2OyBAZWRnZXI7IEBrbml0cl0KCkV2ZW4gdGhvdWdoIHRoaXMgc2F5IEZEUiwgYnV0IHNpbmNlIHdlIGFyZSB1c2luZyB0aGUgc2FtZSBtdWx0aXBsZSBoeXBvdGhlc2lzIAp0ZXN0aW5nIG1ldGhvZCBmb3IgYm90aCBlZGdlUiBhbmQgbGltbWEsIHRoZXkgYXJlIHRoZSBzYW1lIHR5cGUgb2YgY29ycmVjdGVkCnZhbHVlcy4gVGhlIGZpcnN0IG9uZSBpcyBnZW5lIGJlZm9yZSBjb3JyZWN0aW9uLCBhbmQgc2Vjb25kIG9uZSBpcyBhZnRlciBjb3JyZWN0aW9uCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnFsZl9vdXRwdXRfaGl0cyA8LSBlZGdlUjo6dG9wVGFncyhxbGYucG9zX3ZzX25lZyxzb3J0LmJ5ID0gIlBWYWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG4gPSBucm93KG5vcm1hbGl6ZWRfY291bnRfZGF0YSkpCmxlbmd0aCh3aGljaChxbGZfb3V0cHV0X2hpdHMkdGFibGUkUFZhbHVlIDwgMC4wNSkpCmxlbmd0aCh3aGljaChxbGZfb3V0cHV0X2hpdHMkdGFibGUkRkRSIDwgMC4wNSkpCmBgYApbQGw2OyBAZWRnZXJdCgojIyBPcmdhbm9pZCBtb2RlbAoKVGhlIG9yZ2Fub2lkIE1vZGVsIGlzIHdoZXJlIHRoZSBtb2RlbCBhY2NvdW50cyBmb3IgdGhlIGNvbmRpdGlvbiAoY29udHJvbCB2cyAKZXhwZXJpbWVudCksIGFuZCB0aGUgZGF5IHRoYXQgdGhlIGRhdGEgd2FzIGNvbGxlY3RlZCBvbiwgYW5kIApmb3Igd2hpY2ggb3JnYW5vaWRzIGlzIHRoZSBkYXRhIGNvbGxlY3RlZCBmcm9tLiAKCmBgYHtyIHdhcm5pbmc9RkFMU0V9CmZpbHRlcmVkX2RhdGFfbWF0cml4IDwtIGFzLm1hdHJpeChDUlhfRXhwZXJpbWVudFJhd0NvdW50RmlsdGVyZWRbLDM6MjhdKQpyb3duYW1lcyhmaWx0ZXJlZF9kYXRhX21hdHJpeCkgPC0gQ1JYX0V4cGVyaW1lbnRSYXdDb3VudEZpbHRlcmVkJGVucy5pZApkIDwtIGVkZ2VSOjpER0VMaXN0KGNvdW50cz1maWx0ZXJlZF9kYXRhX21hdHJpeCwgZ3JvdXA9c2FtcGxlcyRjb25kaXRpb24pCmRfb3JnIDwtIGVkZ2VSOjplc3RpbWF0ZURpc3AoZCwgbW9kZWxfZGVzaWduX29yZykKZml0X29yZyA8LSBlZGdlUjo6Z2xtUUxGaXQoZF9vcmcsIG1vZGVsX2Rlc2lnbl9vcmcpCgpgYGAKW0BsNjsgQGVkZ2VyXQoKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy5jYXA9J1RhYmxlIDc6IFNob3cgdGhlIG91dHB1dCBvZiBnbG1RTEZpdCBhZnRlciBmaXR0aW5nIHRoZSBkYXRhIHRvIHRoZSBvcmdhbm9pZCBtb2RlbCwgY2FsY3VsYXRpbmcgdGhlIHAtdmFsdWUsIGFuZCBjb3JyZWN0aW5nIHRoZSBwLXZhbHVlLiBUaGlzIGlzIG9ubHkgc2hvd2luZyB0aGUgZmlyc3QgMTAsIG9yZGVyZWQgYnkgcC12YWx1ZS4gTm90ZSB0aGF0IHRoZSBwIHZhbHVlIGFuZCBGRFIgYXJlIG5vdCBhY3R1YWxseSAwLCB0aGV5IGFyZSBqdXN0IHJvdW5kZWQgdG8gMCd9CnFsZi5wb3NfdnNfbmVnIDwtIGVkZ2VSOjpnbG1RTEZUZXN0KGZpdF9vcmcsIGNvZWY9J3NhbXBsZXMkY29uZGl0aW9uQ1JYTENBJykKa25pdHI6OmthYmxlKGVkZ2VSOjp0b3BUYWdzKHFsZi5wb3NfdnNfbmVnKSwgdHlwZT0iaHRtbCIscm93Lm5hbWVzID0gRkFMU0UpCmBgYAoKCltAbDY7IEBrbml0cjsgQGVkZ2VyXQoKVGhlIGZpcnN0IG9uZSBpcyBnZW5lIGJlZm9yZSBjb3JyZWN0aW9uLCBhbmQgc2Vjb25kIG9uZSBpcyBhZnRlciBjb3JyZWN0aW9uCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnFsZl9vdXRwdXRfaGl0c19vcmcgPC0gZWRnZVI6OnRvcFRhZ3MocWxmLnBvc192c19uZWcsc29ydC5ieSA9ICJQVmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICBuID0gbnJvdyhub3JtYWxpemVkX2NvdW50X2RhdGEpKQpsZW5ndGgod2hpY2gocWxmX291dHB1dF9oaXRzX29yZyR0YWJsZSRQVmFsdWUgPCAwLjA1KSkKbGVuZ3RoKHdoaWNoKHFsZl9vdXRwdXRfaGl0c19vcmckdGFibGUkRkRSIDwgMC4wNSkpCmBgYApbQGw2OyBAZWRnZXJdCgojIyBDb21wYXJlIDIgbW9kZWwgZnJvbSBlZGdlUgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLmNhcD0nRmlndXJlIDg6IFNob3dpbmcgZWFjaCBnZW5lIG9uIGEgZ3JhcGggd2hlcmUgdGhlIGNvb3JkaW5hdGVzIGlzIHRoZSBjb3JyZWN0ZWQgcC12YWx1ZXMgdXNpbmcgdGhlIHNpbXBsZSBtb2RlbCBhbmQgdGhlIG9yZ2Fub2lkIG1vZGVsIGluIGdsbVFMRml0LCB6b29tZWQgaW4gb24gMC0wLjEuIEhpZ2hsaWdodGluZyBzaWduaWZpY2FudCBnZW5lcyBpbiBib3RoIG1vZGVsIGFzIHJlZCwgaW4gc2ltcGxlIG1vZGVsIG9ubHkgYXMgb3JhbmdlLCBpbiBvcmdhbm9pZCBtb2RlbCBvbmx5IGFzIHJlZCwgYW5kIG5vbi1zaWduaWZpY2FudCBnZW5lcyBhcyBibGFjayd9CnNpbXBsZV9tb2RlbF9wdmFsdWVzX3FsZiA8LSBkYXRhLmZyYW1lKGVuc2VtYmxfaWQgPQogIHJvd25hbWVzKHFsZl9vdXRwdXRfaGl0cyR0YWJsZSksCiAgc2ltcGxlX3B2YWx1ZSA9IHFsZl9vdXRwdXRfaGl0cyR0YWJsZSRGRFIpCgpvcmdfbW9kZWxfcHZhbHVlc19xbGYgPC0gIGRhdGEuZnJhbWUoZW5zZW1ibF9pZCA9CiAgcm93bmFtZXMocWxmX291dHB1dF9oaXRzX29yZyR0YWJsZSksCiAgb3JnYW5vaWRfcHZhbHVlID0gcWxmX291dHB1dF9oaXRzX29yZyR0YWJsZSRGRFIpCgp0d29fbW9kZWxzX3B2YWx1ZXMgPC0gbWVyZ2Uoc2ltcGxlX21vZGVsX3B2YWx1ZXNfcWxmLAogIG9yZ19tb2RlbF9wdmFsdWVzX3FsZiwgYnkueD0xLCBieS55PTEpCgoKIyBzYW1lIGNvbG9yIHNldHRpbmcgYXMgYWJvdmUKdHdvX21vZGVsc19wdmFsdWVzJGNvbG91ciA8LSAiYmxhY2siCgp0d29fbW9kZWxzX3B2YWx1ZXMkY29sb3VyWwogIHR3b19tb2RlbHNfcHZhbHVlcyRzaW1wbGVfcHZhbHVlPDAuMDVdIDwtICJvcmFuZ2UiCgp0d29fbW9kZWxzX3B2YWx1ZXMkY29sb3VyWwogIHR3b19tb2RlbHNfcHZhbHVlcyRvcmdhbm9pZF9wdmFsdWU8MC4wNV0gPC0gImJsdWUiCgp0d29fbW9kZWxzX3B2YWx1ZXMkY29sb3VyWwogIHR3b19tb2RlbHNfcHZhbHVlcyRzaW1wbGVfcHZhbHVlPDAuMDUgJgogIHR3b19tb2RlbHNfcHZhbHVlcyRvcmdhbm9pZF9wdmFsdWU8MC4wNV0gPC0gInJlZCIKCnBsb3QodHdvX21vZGVsc19wdmFsdWVzJHNpbXBsZV9wdmFsdWUsCiAgICAgdHdvX21vZGVsc19wdmFsdWVzJG9yZ2Fub2lkX3B2YWx1ZSwKICAgICBjb2wgPSB0d29fbW9kZWxzX3B2YWx1ZXMkY29sb3VyLAogICAgIHhsYWIgPSAiU2ltcGxlIG1vZGVsIGFkanVzdGVkIHAtdmFsdWVzIiwKICAgICB5bGFiID0iT3JnYW5vaWQgbW9kZWwgYWRqdXN0ZWQgcC12YWx1ZXMiLAogICAgIG1haW49IlNpbXBsZSB2cyBPcmdhbm9pZCBlZGdlUiIsCiAgICAgeGxpbT1jKDAsIDAuMSksCiAgICAgeWxpbT1jKDAsIDAuMSkpCmBgYApGaWd1cmUgODogU2hvd2luZyBlYWNoIGdlbmUgb24gYSBncmFwaCB3aGVyZSB0aGUgY29vcmRpbmF0ZXMgaXMgdGhlIGNvcnJlY3RlZCBwLXZhbHVlcyB1c2luZyB0aGUgc2ltcGxlIG1vZGVsIGFuZCB0aGUgb3JnYW5vaWQgbW9kZWwgaW4gZ2xtUUxGaXQsIHpvb21lZCBpbiBvbiAwLTAuMS4gSGlnaGxpZ2h0aW5nIHNpZ25pZmljYW50IGdlbmVzIGluIGJvdGggbW9kZWwgYXMgcmVkLCBpbiBzaW1wbGUgbW9kZWwgb25seSBhcyBvcmFuZ2UsIGluIG9yZ2Fub2lkIG1vZGVsIG9ubHkgYXMgcmVkLCBhbmQgbm9uLXNpZ25pZmljYW50IGdlbmVzIGFzIGJsYWNrCltAbDZdCgpUaGUgbWFqb3JpdHkgb2YgZ2VuZXMgdGhhdCBoYXZlIGxlc3MgdGhhbiAwLjA1IHAtdmFsdWUgaW4gZWl0aGVyIG1vZGVsLCBoYXMgbGVzcyB0aGFuIDAuMDUgCnAtdmFsdWUgaW4gYm90aCBtb2RlbC4KClRvIGRldGVybWluZSB3aGljaCBtb2RlbCBpcyBtb3JlIGFwcHJvcHJpYXRlLCB3ZSB0YWtlIGEgbG9vayBhdCBwPXZhbHVlIG9mIHRoZQpnZW5lIG9mIGludGVyZXN0LCBDUlguIAoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLmNhcD0nRmlndXJlIDk6IFNob3dpbmcgZWFjaCBnZW5lIG9uIGEgZ3JhcGggd2hlcmUgdGhlIGNvb3JkaW5hdGVzIGlzIHRoZSBjb3JyZWN0ZWQgcC12YWx1ZXMgdXNpbmcgdGhlIHNpbXBsZSBtb2RlbCBhbmQgdGhlIG9yZ2Fub2lkIG1vZGVsIGluIGdsbVFMRml0LCB6b29tZWQgaW4gb24gMC0wLjAxLiBIaWdobGlnaHRpbmcgb25seSB0aGUgZ2VuZSBvZiBpbnRlcmVzdCwgQ1JYJ30KIyBzYW1lIGFzIGFib3ZlLCBidXQgem9vbWVkIGluIHRvIHNob3cgZ2VuZSBvZiBpbnRlcmVzdAoKCmVuc2VtYmxfb2ZfaW50ZXJlc3QgPC0gbm9ybWFsaXplZF9jb3VudF9kYXRhJGVuc2VtYmxfZ2VuZV9pZFsKICB3aGljaChub3JtYWxpemVkX2NvdW50X2RhdGEkaGduY19zeW1ib2wgPT0gIkNSWCIpXQp0d29fbW9kZWxzX3B2YWx1ZXMkY29sb3VyIDwtICJncmV5Igp0d29fbW9kZWxzX3B2YWx1ZXMkY29sb3VyW3R3b19tb2RlbHNfcHZhbHVlcyRlbnNlbWJsX2lkPT0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVuc2VtYmxfb2ZfaW50ZXJlc3RdIDwtICJyZWQiCnBsb3QodHdvX21vZGVsc19wdmFsdWVzJHNpbXBsZV9wdmFsdWUsCiAgICAgdHdvX21vZGVsc19wdmFsdWVzJG9yZ2Fub2lkX3B2YWx1ZSwKICAgICBjb2wgPSB0d29fbW9kZWxzX3B2YWx1ZXMkY29sb3VyLAogICAgIHhsYWIgPSAic2ltcGxlIG1vZGVsIGFkanVzdGVkIHAtdmFsdWVzIiwKICAgICB5bGFiID0ib3JnYW5vaWQgbW9kZWwgYWRqdXN0ZWQgcC12YWx1ZXMiLAogICAgIG1haW49IlNpbXBsZSB2cyBPcmdhbm9pZCBMaW1tYSIsCiAgICAgeGxpbSA9IGMoMCwgMC4wMSksCiAgICAgeWxpbSA9IGMoMCwgMC4wMSkpCnBvaW50cyh0d29fbW9kZWxzX3B2YWx1ZXNbd2hpY2goCiAgdHdvX21vZGVsc19wdmFsdWVzJGVuc2VtYmxfaWQgPT0gZW5zZW1ibF9vZl9pbnRlcmVzdCksMjozXSwKICAgICAgIHBjaD0yMCwgY29sPSJyZWQiLCBjZXg9MS41KQpsZWdlbmQoMCwxLGxlZ2VuZD1jKCJDUlgiLCJyZXN0IiksCiAgICAgICBmaWxsPWMoInJlZCIsImdyZXkiKSxjZXggPSAwLjcpCmBgYApGaWd1cmUgOTogU2hvd2luZyBlYWNoIGdlbmUgb24gYSBncmFwaCB3aGVyZSB0aGUgY29vcmRpbmF0ZXMgaXMgdGhlIGNvcnJlY3RlZCBwLXZhbHVlcyB1c2luZyB0aGUgc2ltcGxlIG1vZGVsIGFuZCB0aGUgb3JnYW5vaWQgbW9kZWwgaW4gZ2xtUUxGaXQsIHpvb21lZCBpbiBvbiAwLTAuMDEuIEhpZ2hsaWdodGluZyBvbmx5IHRoZSBnZW5lIG9mIGludGVyZXN0LCBDUlgKW0BsNl0KClRoZSBzaW1wbGUgbW9kZWwgaXMgYmV0dGVyLCBJIGJlbGlldmUgdGhpcyBpcyBzdGlsbCBkdWUgdG8gdGhlIHNhbWUgcmVhc29uIHRoYXQsCnRoZXJlIGFyZSBtaW5pbWFsIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgcmV0aW5hbCBvcmdhbm9pZHMsIHNpbmNlIHRoZXkgYXJlIApkZXJpdmVkIGZyb20gaVBTQyB0aGF0IGlzIGZyb20gdGhlIHNhbWUgcGF0aWVudC4gCgpgYGB7cn0KcWxmX291dHB1dF9oaXRzJHRhYmxlJGxvZ0ZDW3doaWNoKHJvd25hbWVzKHFsZl9vdXRwdXRfaGl0cyR0YWJsZSkgPT0gZW5zZW1ibF9vZl9pbnRlcmVzdCldCmBgYAoKYGBge3J9CnFsZl9vdXRwdXRfaGl0c19vcmckdGFibGUkbG9nRkNbd2hpY2gocm93bmFtZXMocWxmX291dHB1dF9oaXRzJHRhYmxlKSA9PSBlbnNlbWJsX29mX2ludGVyZXN0KV0KYGBgCgoKIyMgQ29tcGFyZSBsaW1tYSB3aXRoIHF1YXNpLWxpa2VsaWhvb2QKCgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLmNhcD0nRmlndXJlIDEwOiBTaG93aW5nIGVhY2ggZ2VuZSBvbiBhIGdyYXBoIHdoZXJlIHRoZSBjb29yZGluYXRlcyBpcyB0aGUgY29ycmVjdGVkIHAtdmFsdWVzIHVzaW5nIHRoZSBzaW1wbGUgbW9kZWwgaW4gZ2xtUUxGaXQgYW5kIGluIGxpbW1hLiBIaWdobGlnaHRpbmcgc2lnbmlmaWNhbnQgZ2VuZXMgaW4gYm90aCBtb2RlbCBhcyByZWQsIGluIHNpbXBsZSBtb2RlbCBvbmx5IGFzIG9yYW5nZSwgaW4gb3JnYW5vaWQgbW9kZWwgb25seSBhcyByZWQsIGFuZCBub24tc2lnbmlmaWNhbnQgZ2VuZXMgYXMgYmxhY2snfQpxbGZfc2ltcGxlX21vZGVsX3B2YWx1ZXMgPC0gZGF0YS5mcmFtZSgKICAgICAgICAgIGVuc2VtYmxfaWQgPSByb3duYW1lcyhxbGZfb3V0cHV0X2hpdHMkdGFibGUpLAogICAgICAgICAgcWxmX3NpbXBsZV9wdmFsdWUgPSBxbGZfb3V0cHV0X2hpdHMkdGFibGUkRkRSKQoKbGltbWFfc2ltcGxlX21vZGVsX3B2YWx1ZXMgPC0gIGRhdGEuZnJhbWUoCiAgICAgICAgICBlbnNlbWJsX2lkID0gb3V0cHV0X2hpdHMkZW5zZW1ibF9nZW5lX2lkLAogICAgICAgICAgbGltbWFfc2ltcGxlX3B2YWx1ZSA9IG91dHB1dF9oaXRzJGFkai5QLlZhbCkKCnR3b19tb2RlbHNfcHZhbHVlcyA8LSBtZXJnZShxbGZfc2ltcGxlX21vZGVsX3B2YWx1ZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaW1tYV9zaW1wbGVfbW9kZWxfcHZhbHVlcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5Lng9MSxieS55PTEpCgojIHNhbWUgY29sb3Igc2V0dGluZyBhcyBhYm92ZQp0d29fbW9kZWxzX3B2YWx1ZXMkY29sb3VyIDwtICJibGFjayIKCnR3b19tb2RlbHNfcHZhbHVlcyRjb2xvdXJbdHdvX21vZGVsc19wdmFsdWVzJHFsZl9zaW1wbGVfcHZhbHVlCiAgICAgICAgICAgICAgICAgICAgICAgICAgPDAuMDVdIDwtICJvcmFuZ2UiCgp0d29fbW9kZWxzX3B2YWx1ZXMkY29sb3VyW3R3b19tb2RlbHNfcHZhbHVlcyRsaW1tYV9zaW1wbGVfcHZhbHVlCiAgICAgICAgICAgICAgICAgICAgICAgICAgPDAuMDVdIDwtICJibHVlIgoKdHdvX21vZGVsc19wdmFsdWVzJGNvbG91clt0d29fbW9kZWxzX3B2YWx1ZXMkcWxmX3NpbXBsZV9wdmFsdWUKICAgICAgICAgICAgICAgICAgICAgICAgICA8MC4wNSAmCnR3b19tb2RlbHNfcHZhbHVlcyRsaW1tYV9zaW1wbGVfcHZhbHVlPDAuMDVdIDwtICJyZWQiCgpwbG90KHR3b19tb2RlbHNfcHZhbHVlcyRxbGZfc2ltcGxlX3B2YWx1ZSwKICAgICB0d29fbW9kZWxzX3B2YWx1ZXMkbGltbWFfc2ltcGxlX3B2YWx1ZSwKICAgICBjb2wgPSB0d29fbW9kZWxzX3B2YWx1ZXMkY29sb3VyLAogICAgIHhsYWIgPSAiUUxGIHNpbXBsZSBtb2RlbCBhZGp1c3RlZCBwLXZhbHVlcyIsCiAgICAgeWxhYiA9IkxpbW1hIHNpbXBsZSBtb2RlbCBhZGp1c3RlZCBwLXZhbHVlcyIsCiAgICAgbWFpbj0iUUxGIHZzIExpbW1hIiwKICAgICB4bGltID0gYygwLCAxKSwKICAgICB5bGltID0gYygwLCAxKSkKYGBgCkZpZ3VyZSAxMDogU2hvd2luZyBlYWNoIGdlbmUgb24gYSBncmFwaCB3aGVyZSB0aGUgY29vcmRpbmF0ZXMgaXMgdGhlIGNvcnJlY3RlZCBwLXZhbHVlcyB1c2luZyB0aGUgc2ltcGxlIG1vZGVsIGluIGdsbVFMRml0IGFuZCBpbiBsaW1tYS4gSGlnaGxpZ2h0aW5nIHNpZ25pZmljYW50IGdlbmVzIGluIGJvdGggbW9kZWwgYXMgcmVkLCBpbiBzaW1wbGUgbW9kZWwgb25seSBhcyBvcmFuZ2UsIGluIG9yZ2Fub2lkIG1vZGVsIG9ubHkgYXMgcmVkLCBhbmQgbm9uLXNpZ25pZmljYW50IGdlbmVzIGFzIGJsYWNrCltAbDZdCgoKCkZvciBzb21lIHJlYXNvbiwgdGhpcyBpcyBhbGwgb3ZlciB0aGUgcGxhY2UKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy5jYXA9J0ZpZ3VyZSAxMTogU2hvd2luZyBlYWNoIGdlbmUgb24gYSBncmFwaCB3aGVyZSB0aGUgY29vcmRpbmF0ZXMgaXMgdGhlIGNvcnJlY3RlZCBwLXZhbHVlcyB1c2luZyB0aGUgc2ltcGxlIG1vZGVsIGluIGdsbVFMRml0IGFuZCBpbiBsaW1tYSwgem9vbWVkIGluIG9uIDAtMC4wMi4gSGlnaGxpZ2h0aW5nIG9ubHkgdGhlIGdlbmUgb2YgaW50ZXJlc3QsIENSWCd9CiMgem9vbWVkIGluIHRvIHNob3cgCmVuc2VtYmxfb2ZfaW50ZXJlc3QgPC0gbm9ybWFsaXplZF9jb3VudF9kYXRhJGVuc2VtYmxfZ2VuZV9pZFsKICB3aGljaChub3JtYWxpemVkX2NvdW50X2RhdGEkaGduY19zeW1ib2wgPT0gIkNSWCIpXQoKdHdvX21vZGVsc19wdmFsdWVzJGNvbG91ciA8LSAiZ3JleSIKCnR3b19tb2RlbHNfcHZhbHVlcyRjb2xvdXJbdHdvX21vZGVsc19wdmFsdWVzJGVuc2VtYmxfaWQKICAgICAgICAgICAgICAgICAgICAgICAgICA9PWVuc2VtYmxfb2ZfaW50ZXJlc3RdIDwtICJyZWQiCgpwbG90KHR3b19tb2RlbHNfcHZhbHVlcyRxbGZfc2ltcGxlX3B2YWx1ZSwKICAgICB0d29fbW9kZWxzX3B2YWx1ZXMkbGltbWFfc2ltcGxlX3B2YWx1ZSwKICAgICBjb2wgPSB0d29fbW9kZWxzX3B2YWx1ZXMkY29sb3VyLAogICAgIHhsYWIgPSAiUUxGIHNpbXBsZSBtb2RlbCBhZGp1c3RlZCBwLXZhbHVlcyIsCiAgICAgeWxhYiA9IkxpbW1hIHNpbXBsZSBtb2RlbCBhZGp1c3RlZCBwLXZhbHVlcyIsCiAgICAgbWFpbj0iUUxGIHZzIExpbW1hIiwKICAgICB4bGltID0gYygwLCAwLjAyKSwKICAgICB5bGltID0gYygwLCAwLjAyKSkKCnBvaW50cyh0d29fbW9kZWxzX3B2YWx1ZXNbCiAgdHdvX21vZGVsc19wdmFsdWVzJGVuc2VtYmxfaWQ9PWVuc2VtYmxfb2ZfaW50ZXJlc3QsMjozXSwKICAgICAgIHBjaD0yNCwgIGNvbD0icmVkIiwgY2V4PTEuNSkKYGBgCkZpZ3VyZSAxMTogU2hvd2luZyBlYWNoIGdlbmUgb24gYSBncmFwaCB3aGVyZSB0aGUgY29vcmRpbmF0ZXMgaXMgdGhlIGNvcnJlY3RlZCBwLXZhbHVlcyB1c2luZyB0aGUgc2ltcGxlIG1vZGVsIGluIGdsbVFMRml0IGFuZCBpbiBsaW1tYSwgem9vbWVkIGluIG9uIDAtMC4wMi4gSGlnaGxpZ2h0aW5nIG9ubHkgdGhlIGdlbmUgb2YgaW50ZXJlc3QsIENSWApbQGw2XQoKQWx0aG91Z2ggbGltbWEgd2FzIG1hZGUgZm9yIG1pY3JvYXJyYXksIGFuZCBJIGFtIHVzaW5nIGJ1bGsgUk5BLXNlcSBkYXRhLCB3aGljaCBlZGdlUiBtZXRob2RzCmFyZSBkZXNpZ25lZCBmb3IsIGluIGVkZ2VSIGl0IHNob3dzIHRoYXQgQ1JYIGlzIGFjdHVhbGx5IHVwcmVndWxhdGVkLCBidXQgaXQgCnNob3VsZCBiZSBkb3duIHJlZ3VsYXRlZC4KCgojIyBIZWF0bWFwIGZyb20gZWRnZVIKCmhlYXRtYXAsIGJ1dCB1c2luZyB0b3BoaXRzIGZyb20gcWxmIGluc3RlYWQgb2YgbGltbWEgCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy5jYXA9J0ZpZ3VyZSAxMjogSGVhdG1hcCBmb3IgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gb2YgdGhlIGZpcnN0IDEwMDAgZ2VuZXMsIG9yZGVyZWQgYnkgZW5zZW1ibCBpZCwgYWZ0ZXIgcm93IG5vcm1hbGl6YXRpb24gZm9yIHNpZ25pZmljYW50IGdlbmVzIHVzaW5nIHRoZSBzaW1wbGUgbW9kZWwgaW4gZ2xtUUxGaXQuIE9ubHkgdGhlIGZpcnN0IDEwMDAgZ2VuZXMgYXJlIHNob3duIGR1ZSB0byByZW5kZXJpbmcgcmVzb3VyY2UgaXNzdWVzJ30KdG9wX2hpdHMgPC0gcm93bmFtZXMocWxmX291dHB1dF9oaXRzJHRhYmxlKVsKICBxbGZfb3V0cHV0X2hpdHMkdGFibGUkRkRSPDAuMDVdCgpoZWF0bWFwX21hdHJpeF90b3BoaXRzIDwtIHQoCiAgc2NhbGUodChoZWF0bWFwX21hdHJpeFt3aGljaChyb3duYW1lcyhoZWF0bWFwX21hdHJpeCkKJWluJSB0b3BfaGl0cyksXSkpKQoKbnJvdyhoZWF0bWFwX21hdHJpeF90b3BoaXRzKQoKaWYobWluKGhlYXRtYXBfbWF0cml4X3RvcGhpdHMpID09IDApewpoZWF0bWFwX2NvbCA9IGNvbG9yUmFtcDIoYyggMCwgbWF4KGhlYXRtYXBfbWF0cml4X3RvcGhpdHMpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjKCAid2hpdGUiLCAicmVkIikpCn0gZWxzZSB7CmhlYXRtYXBfY29sID0gY29sb3JSYW1wMihjKG1pbihoZWF0bWFwX21hdHJpeF90b3BoaXRzKSwgMCwKICAgICAgICAgICAgICAgICAgICAgIG1heChoZWF0bWFwX21hdHJpeF90b3BoaXRzKSksCiAgICAgICAgICAgICAgICAgICAgICBjKCJibHVlIiwgIndoaXRlIiwgInJlZCIpKQp9CgpIZWF0bWFwKGFzLm1hdHJpeChoZWF0bWFwX21hdHJpeF90b3BoaXRzWzE6MTAwMCxdKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9yb3dzID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9jb2x1bW5zID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfcm93X2RlbmQgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19jb2x1bW5fZGVuZCA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2w9aGVhdG1hcF9jb2wsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaG93X2NvbHVtbl9uYW1lcyA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaG93X3Jvd19uYW1lcyA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19oZWF0bWFwX2xlZ2VuZCA9IFRSVUUsIAogICAgICAgIGhlYXRtYXBfbGVnZW5kX3BhcmFtID0gbGlzdCh0aXRsZSA9ICJnbG1RTEZpdCBIZWF0bWFwIikKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKYGBgCkZpZ3VyZSAxMjogSGVhdG1hcCBmb3IgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gb2YgdGhlIGZpcnN0IDEwMDAgZ2VuZXMsIG9yZGVyZWQgYnkgZW5zZW1ibCBpZCwgYWZ0ZXIgcm93IG5vcm1hbGl6YXRpb24gZm9yIHNpZ25pZmljYW50IGdlbmVzIHVzaW5nIHRoZSBzaW1wbGUgbW9kZWwgaW4gZ2xtUUxGaXQuIE9ubHkgdGhlIGZpcnN0IDEwMDAgZ2VuZXMgYXJlIHNob3duIGR1ZSB0byByZW5kZXJpbmcgcmVzb3VyY2UgaXNzdWVzCltAbDY7IEBoZWF0bWFwOyBAY29sb3JdCgpjb21wYXJlIHRvIHRoZSBoZWF0bWFwIGZyb20gbGVtbWEsIHRoZSBkYXkgMjAwIGRvIG5vdCBjbHVzdGVyIHRvZ2V0aGVyLCAKYnV0IHRoZSBkYXkgMTUwIGFuZCBkYXkgMTI1IGNsdXN0ZXIgaW50byAyIGdyb3Vwcy4gSG93ZXZlciBhbGwgZGF5IDkwIGRvIGNsdXN0ZXIKdG9nZXRoZXIuIAoKdGhlbiBhbHNvIGNsdXN0ZXIgYnkgY29udHJvbCB2cyBleHBlcmltZW50CgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcuY2FwPSdGaWd1cmUgMTM6IEhlYXRtYXAgZm9yIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIG9mIHRoZSBmaXJzdCAxMDAwIGdlbmVzLCBvcmRlcmVkIGJ5IGVuc2VtYmwgaWQsIGFmdGVyIHJvdyBub3JtYWxpemF0aW9uIGZvciBzaWduaWZpY2FudCBnZW5lcyB1c2luZyB0aGUgc2ltcGxlIG1vZGVsIGluIGdsbVFMRml0LCB3aXRob3V0IGNsdXN0ZXJpbmcsIGFuZCBncm91cCBjb250cm9sIGFuZCBleHBlcmltZW50IHRvZ2V0aGVyLCByZXNwZWN0aXZlbHkuIE9ubHkgdGhlIGZpcnN0IDEwMDAgZ2VuZXMgYXJlIHNob3duIGR1ZSB0byByZW5kZXJpbmcgcmVzb3VyY2UgaXNzdWVzJ30KdG9wX2hpdHMgPC0gcm93bmFtZXMocWxmX291dHB1dF9oaXRzJHRhYmxlKVtxbGZfb3V0cHV0X2hpdHMkdGFibGUkRkRSCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPDAuMDVdCmhlYXRtYXBfbWF0cml4X3RvcGhpdHMgPC0gdCgKICBzY2FsZSh0KGhlYXRtYXBfbWF0cml4W3doaWNoKHJvd25hbWVzKGhlYXRtYXBfbWF0cml4KQolaW4lIHRvcF9oaXRzKSxdKSkpCgpoZWF0bWFwX21hdHJpeF90b3BoaXRzPC0gaGVhdG1hcF9tYXRyaXhfdG9waGl0c1ssIApjKGdyZXAoY29sbmFtZXMoaGVhdG1hcF9tYXRyaXhfdG9waGl0cykscGF0dGVybiA9ICJcXENSWExDQSIpLAogIGdyZXAoY29sbmFtZXMoaGVhdG1hcF9tYXRyaXhfdG9waGl0cykscGF0dGVybiA9ICJcXENvbnRyb2wiKSldCgpucm93KGhlYXRtYXBfbWF0cml4X3RvcGhpdHMpCgppZihtaW4oaGVhdG1hcF9tYXRyaXhfdG9waGl0cykgPT0gMCl7CmhlYXRtYXBfY29sID0gY29sb3JSYW1wMihjKCAwLCBtYXgoaGVhdG1hcF9tYXRyaXhfdG9waGl0cykpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMoICJ3aGl0ZSIsICJyZWQiKSkKfSBlbHNlIHsKaGVhdG1hcF9jb2wgPSBjb2xvclJhbXAyKGMobWluKGhlYXRtYXBfbWF0cml4X3RvcGhpdHMpLCAwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWF4KGhlYXRtYXBfbWF0cml4X3RvcGhpdHMpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjKCJibHVlIiwgIndoaXRlIiwgInJlZCIpKQp9CgpIZWF0bWFwKGFzLm1hdHJpeChoZWF0bWFwX21hdHJpeF90b3BoaXRzWzE6MTAwMCxdKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9yb3dzID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9jb2x1bW5zID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaG93X3Jvd19kZW5kID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfY29sdW1uX2RlbmQgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbD1oZWF0bWFwX2NvbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfY29sdW1uX25hbWVzID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfcm93X25hbWVzID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaG93X2hlYXRtYXBfbGVnZW5kID0gVFJVRSwgCiAgICAgICAgaGVhdG1hcF9sZWdlbmRfcGFyYW0gPSBsaXN0KHRpdGxlID0gImdsbVFMRml0IEhlYXRtYXAgbm8gY2x1c3RlciIpKQpgYGAKRmlndXJlIDEzOiBIZWF0bWFwIGZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBvZiB0aGUgZmlyc3QgMTAwMCBnZW5lcywgb3JkZXJlZCBieSBlbnNlbWJsIGlkLCBhZnRlciByb3cgbm9ybWFsaXphdGlvbiBmb3Igc2lnbmlmaWNhbnQgZ2VuZXMgdXNpbmcgdGhlIHNpbXBsZSBtb2RlbCBpbiBnbG1RTEZpdCwgd2l0aG91dCBjbHVzdGVyaW5nLCBhbmQgZ3JvdXAgY29udHJvbCBhbmQgZXhwZXJpbWVudCB0b2dldGhlciwgcmVzcGVjdGl2ZWx5LiBPbmx5IHRoZSBmaXJzdCAxMDAwIGdlbmVzIGFyZSBzaG93biBkdWUgdG8gcmVuZGVyaW5nIHJlc291cmNlIGlzc3VlcwpbQGw2OyBAaGVhdG1hcDsgQGNvbG9yXQoKIyBPdmVyIHJlcHJlc2VudGF0aW9uIGFuYWx5c2lzCgoKVGhpcyBpcyB0aGUgbnVtYmVyIG9mIGdlbmVzIHVwcmVndWxhdGVkIGFuZCBkb3ducmVndWxhdGVkIChJIGRpZCBjaGVjayB0aGUgbW9kZWwKZGVzaWduLCBjb250cm9sIHdhcyBsYWJlbGVkIGFzIDAgYW5kIGV4cGVyaW1lbnQvQ1JYIHdhcyBsYWJlbGVkIGFzIDEpLCBzbyAKdGhpcyBkb2VzIG1lYW4gdGhhdCB0aGF0IGlzIHVwcmVndWxhdGVkIGFuZCBkb3ducmVndWxhdGVkIGluIGV4cGVyaW1lbnQuCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9Cmxlbmd0aCh3aGljaChvdXRwdXRfaGl0cyRhZGouUC5WYWwgPCAwLjA1IAogICAgICAgICAgICAgJiBvdXRwdXRfaGl0cyRsb2dGQyA+IDApKQpsZW5ndGgod2hpY2gob3V0cHV0X2hpdHMkYWRqLlAuVmFsIDwgMC4wNSAKICAgICAgICAgICAgICYgb3V0cHV0X2hpdHMkbG9nRkMgPCAwKSkKYGBgCltAbDddCgpJIG9ubHkgb3V0cHV0dGVkIHRoZSBmaXJzdCA1MDAwIGdlbmVzLCBiZWNhdXNlIGc6cHJvZmlsZXIgd2lsbCBjcmFzaCB3aXRoIHRvbyAKbWFueSBnZW5lcy4gCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQoKb3V0cHV0X2hpdHNbLCJyYW5rIl0gPC0gKC1sb2cob3V0cHV0X2hpdHMkYWRqLlAuVmFsLCBiYXNlID0xMCkgKiBzaWduKG91dHB1dF9oaXRzJGxvZ0ZDKSkKCm91dHB1dF9oaXRzIDwtIG91dHB1dF9oaXRzW29yZGVyKG91dHB1dF9oaXRzJHJhbmspLF0KCnVwcmVndWxhdGVkX2dlbmVzIDwtIG91dHB1dF9oaXRzJGVuc2VtYmxfZ2VuZV9pZFsKICB3aGljaChvdXRwdXRfaGl0cyRhZGouUC5WYWwgPCAwLjA1IAogICAgICAgICAgICAgJiBvdXRwdXRfaGl0cyRsb2dGQyA+IDApXQoKZG93bnJlZ3VsYXRlZF9nZW5lcyA8LSBvdXRwdXRfaGl0cyRlbnNlbWJsX2dlbmVfaWRbCiAgd2hpY2gob3V0cHV0X2hpdHMkYWRqLlAuVmFsIDwgMC4wNSAKICAgICAgICAgICAgICYgb3V0cHV0X2hpdHMkbG9nRkMgPCAwKV0KCmZpcnN0NTAwMF9nZW5lcyA8LSBvdXRwdXRfaGl0cyRlbnNlbWJsX2dlbmVfaWRbYygxOjUwMDApXQoKaWYgKCFmaWxlLmV4aXN0cygiZGF0YSIpKSB7CiBkaXIuY3JlYXRlKCJkYXRhIikKfQoKd3JpdGUudGFibGUoeD11cHJlZ3VsYXRlZF9nZW5lcywKICAgICAgICAgICAgZmlsZT1maWxlLnBhdGgoImRhdGEiLCJDUlhfdXByZWd1bGF0ZWRfZ2VuZXMudHh0IiksIHNlcCA9ICJcdCIsCiAgICAgICAgICAgIHJvdy5uYW1lcyA9IEZBTFNFLCBjb2wubmFtZXMgPSBGQUxTRSwgcXVvdGUgPSBGQUxTRSkKCndyaXRlLnRhYmxlKHg9ZG93bnJlZ3VsYXRlZF9nZW5lcywKICAgICAgICAgICAgZmlsZT1maWxlLnBhdGgoImRhdGEiLCJDUlhfZG93bnJlZ3VsYXRlZF9nZW5lcy50eHQiKSxzZXAgPSAiXHQiLAogICAgICAgICAgICByb3cubmFtZXMgPSBGQUxTRSwgY29sLm5hbWVzID0gRkFMU0UsIHF1b3RlID0gRkFMU0UpCgp3cml0ZS50YWJsZSh4PWZpcnN0NTAwMF9nZW5lcywKICAgICAgICAgICAgZmlsZT1maWxlLnBhdGgoImRhdGEiLCJDUlhfcmFua2VkX2dlbmVsaXN0LnR4dCIpLAogICAgICAgICAgICBzZXAgPSAiXHQiLAogICAgICAgICAgICByb3cubmFtZXMgPSBGQUxTRSwKICAgICAgICAgICAgY29sLm5hbWVzID0gRkFMU0UsCiAgICAgICAgICAgIHF1b3RlID0gRkFMU0UpCgpgYGAKW0BsN10KCgojIFJlc3VsdHMgb2YgRzpwcm9maWxlcgoKSSBvbmx5IGluY2x1ZGVkIGdlbmVzZXQgdGhhdCBoYXZlIGJldHdlZW4gNS0yMDAgZ2VuZXMKCiFbXSguL3dob2xlbGlzdC5wbmcpCgohW10oLi9kb3ducmVndWxhdGVkLnBuZykKCiFbXSguL3VwcmVndWxhdGVkLnBuZykKCiMgVm9sY2FubyBQbG90CgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpwdmFsdWVzIDwtIGRhdGEuZnJhbWUoCiAgICAgICAgICBlbnNlbWJsX2lkID0gb3V0cHV0X2hpdHMkZW5zZW1ibF9nZW5lX2lkLAogICAgICAgICAgcHZhbHVlcyA9IC1sb2cxMChvdXRwdXRfaGl0cyRhZGouUC5WYWwpKQoKRkMgPC0gIGRhdGEuZnJhbWUoCiAgICAgICAgICBlbnNlbWJsX2lkID0gb3V0cHV0X2hpdHMkZW5zZW1ibF9nZW5lX2lkLAogICAgICAgICAgRkMgPSBvdXRwdXRfaGl0cyRsb2dGQykKCnB2YWx1ZV9GQyA8LSBtZXJnZShwdmFsdWVzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgRkMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBieS54PTEsYnkueT0xKQoKcHZhbHVlX0ZDJGNvbG91ciA8LSAiYmxhY2siCgpwdmFsdWVfRkMkY29sb3VyW3doaWNoKHB2YWx1ZV9GQyRwdmFsdWVzID4gMCldIDwtICJibHVlIgoKcHZhbHVlX0ZDJGNvbG91clt3aGljaChwdmFsdWVfRkMkRkMgPCAwKV0gPC0gInJlZCIKCnBsb3QocHZhbHVlX0ZDJHB2YWx1ZXMsCiAgICAgcHZhbHVlX0ZDJEZDLAogICAgIGNvbCA9IHB2YWx1ZV9GQyRjb2xvdXIsCiAgICAgeGxhYiA9ICJRTEYgLWxvZzEwIGFkanVzdGVkIHB2YWx1ZSIsCiAgICAgeWxhYiA9IlFMRiBsb2cgZm9sZCBjaGFuZ2VzIiwKICAgICBtYWluPSJWb2xjYW5vIHBsb3QgZnJvbSBsaW1tYSIsCiAgICAgeGxpbSA9IGMoMCwgMTApLAogICAgIHlsaW0gPSBjKC01LCA1KSkKYGBgCkZpZ3VyZSAxNDogdm9sY2FubyBwbG90IG9mIGFsbCBnZW5lcywgem9vbWVkIGluIHRvIC01IHRvIDUgZm9yIGxvZ0ZDLCBhbmQgMCB0bwoxMCBmb3IgRkRSIHRvIHNob3cgdGhlIGNoYXJhY3RlcmlzdGljIFUgc2hhcGUuCgpUaGVyZSBpcyB2aXN1YWwgc2VwYXJhdGlvbiBvZiB1cHJlZ3VsYXRlZCBnZW5lcyBhbmQgZG93bnJlZ3VsYXRlZCBnZW5lCgpgYGB7ciBtZXNzYWdlPVRSVUUsIHdhcm5pbmc9RkFMU0V9CgoKcHZhbHVlc19pbnQgPC0gZGF0YS5mcmFtZSgKICAgICAgICAgIGVuc2VtYmxfaWQgPSBvdXRwdXRfaGl0cyRlbnNlbWJsX2dlbmVfaWQsCiAgICAgICAgICBwdmFsdWVzID0gLWxvZzEwKG91dHB1dF9oaXRzJGFkai5QLlZhbCkpCgpGQ19pbnQgPC0gIGRhdGEuZnJhbWUoCiAgICAgICAgICBlbnNlbWJsX2lkID0gb3V0cHV0X2hpdHMkZW5zZW1ibF9nZW5lX2lkLAogICAgICAgICAgRkMgPSBvdXRwdXRfaGl0cyRsb2dGQykKCnB2YWx1ZV9GQ19pbnRlcmVzdCA8LSBtZXJnZShwdmFsdWVzX2ludCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIEZDX2ludCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5Lng9MSxieS55PTEpCgpwdmFsdWVfRkNfaW50ZXJlc3QkY29sb3VyIDwtICJncmV5IgoKcHZhbHVlX0ZDX2ludGVyZXN0JGNvbG91cltwdmFsdWVfRkMkZW5zZW1ibF9pZD09ZW5zZW1ibF9vZl9pbnRlcmVzdF0gPC0gInJlZCIKCnBsb3QocHZhbHVlX0ZDX2ludGVyZXN0JHB2YWx1ZXMsCiAgICAgcHZhbHVlX0ZDX2ludGVyZXN0JEZDLAogICAgIGNvbCA9IHB2YWx1ZV9GQ19pbnRlcmVzdCRjb2xvdXIsCiAgICAgeGxhYiA9ICJRTEYgLWxvZzEwIGFkanVzdGVkIHB2YWx1ZSIsCiAgICAgeWxhYiA9IlFMRiBsb2cgZm9sZCBjaGFuZ2VzIiwKICAgICBtYWluPSJWb2xjYW5vIHBsb3QiKQpgYGAKRmlndXJlIDE1OiB2b2xjYW5vIHBsb3Qgb2YgYWxsIGdlbmVzLCBub3Qgem9vbWVkIGluIHRvIHNob3cgdGhhdCBnZW5lIG9mIGludGVyZXN0CmlzIG5vdCBleHByZXNzZWQKCgojIERpc2N1c3Npb24gCgpXZSBoYXZlIDMgZmFjdG9ycyB0aGF0IHdlIGNhbiBpbmNsdWRlIGluIG91ciBtb2RlbCBkZXNpZ24uIFRoZSBkYXkgdGhhdCB0aGUgZGF5CndhcyBjb2xsZWN0ZWQsIHdoaWNoIG9yZ2Fub2lkIHdhcyBpdCBjb2xsZWN0ZWQgZnJvbSwgaXMgaXQgZXhwZXJpbWVudCBvciBjb250cm9sCnRoYXQgd2UgYXJlIGNvbGxlY3RpbmcgZnJvbT8KCldlIGRvIGhhdmUgdG8gaW5jbHVkZSB0aGUgbGFzdCBvbmUgKGV4cGVyaW1lbnQgdnMgY29udHJvbCksIG9yIGVsc2Ugd2UgYXJlIG5vdApnZXR0aW5nIHRoZSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBmb3IgdGhlIHF1ZXN0aW9uIHRoYXQgdGhlIGF1dGhvcnMgaXMgYXNraW5nLgpbQENSWF0uIEFsdGhvdWdoIGF0IGZpcnN0LCB0aGUgY29udHJvbCBhbmQgZXhwZXJpbWVudHMgZG8gbm90IGRpZmZlciB0aGF0IG11Y2gsCmJ1dCBieSBkYXkgMjAwIHRoZXkgdGhlIGNvbnRyb2xzIGNsdXN0ZXIgdG9nZXRoZXIgbW9yZSB0aGFuIHRoZSBvcmdhbm9pZHMsIApzbyBpcyB0aGUgZXhwZXJpbWVudHMuIEkgYmVsaWV2ZSB3ZSBjYW4gY29uY2x1ZGUgdGhhdCwgYnkgZGF5IDIwMCwgdGhlIGRpZmZlcmVuY2UKZnJvbSBDUlgsIGlzIG1vcmUgdGhhbiB0aGUgb3JnYW5vaWQgdmFyaWFiaWxpdHkuIAoKTG9va2luZyBhdCB0aGUgTURTIHBsb3QgZnJvbSBBMSwgd2UgY2FuIHRlbGwgdGhhdCB0aGUgZGF5IGl0IHdhcyBjb2xsZWN0ZWQgZnJvbQpjaGFuZ2VzIHRoZSBkYXRhIGJ5IGEgbG90LCBzaW5jZSB0aGUgUk5BIGlzIHRha2VuIGZyb20gcmV0aW5hbCBvcmdhbm9pZHMgaW4gCmRldmVsb3BtZW50LCBhbmQgZ2VuZSBleHByZXNzaW9uIGZvciBjZWxsIGluIGRldmVsb3BtZW50IGNoYW5nZXMgZnJvbSBzdGFnZQp0byBzdGFnZS4gVGhlcmVmb3JlIHdlIHNob3VsZCBpbmNsdWRlIHRoaXMuCgpTdGlsbCBsb29rIGF0IHRoZSBNRFMgcGxvdCBmcm9tIEExLCB3aGljaCBvcmdhbm9pZCBpdCB3YXMgY29sbGVjdGVkIGZyb20gZG9lcwpub3QgbWFrZSBtdWNoIG9mIGEgZGlmZmVyZW5jZSwgdGhpcyBpcyBwcm9iYWJseSBiZWNhdXNlIHRoYXQgYWxsIGNvbnRyb2wKb3JnYW5vaWRzIGFyZSBmcm9tIHRoZSBzYW1lIHBlcnNvbiwgYW5kIGFsbCBleHBlcmltZW50IG9yZ2Fub2lkcyBhcmUgZnJvbSB0aGUKc2FtZSBwZXJzb24uIAoKCiMgUXVlc3Rpb25zCgoxLglDYWxjdWxhdGUgcC12YWx1ZXMgZm9yIGVhY2ggb2YgdGhlIGdlbmVzIGluIHlvdXIgZXhwcmVzc2lvbiBzZXQuIEhvdyBtYW55IGdlbmVzIHdlcmUgc2lnbmlmaWNhbnRseSBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQ/IFdoYXQgdGhyZXNob2xkcyBkaWQgeW91IHVzZSBhbmQgd2h5PwoKICAgIEluIHRoZSBtb2RlbCB0aGF0IGRvZXMgbm90IGFjY291bnQgZm9yIG9yZ2Fub2lkIHZhcmlhYmlsaXR5LCB0aGUgbnVtYmVyIG9mIGdlbmVzIHRoYXQgd2hlcmUgc2lnbmlmaWNhbnRseSBkaWZmZXJlbnRpYWxseSBleHByZXNzIHdhcyA4MTc4LiBJbiB0aGUgbW9kZWwgdGhhdCBhY2NvdW50IGZvciBvcmdhbm9pZCB2YXJpYWJpbGl0eSwgdGhlIG51bWJlciBpcyA3ODM4LiBGcm9tIGVkZ2VSLCB3ZSBnb3QgNjU1NiBnZW5lcyB1bmRlciB0aHJlc2hvbGQuIAoKICAgIEkgdXNlZCB0aGUgdGhyZXNob2xkIG9mIDAuMDUsIGJlY2F1c2Ugb2YgY29udmVudGlvbiBhbmQgSSBkbyBub3QgaGF2ZSBhZGRpdGlvbmFsIGtub3dsZWRnZSAoZnJvbSB0aGUgb3JpZ2luYWwgcGFwZXIgb3IgYW55d2hlcmUpIGFib3V0IHdoYXQgdGhyZXNob2xkIHdvdWxkIGdpdmUgbWUgYmVzdCByZXN1bHRzIGZvciBidWxrIFJOQSBzZXEgZGF0YSBmcm9tIHJldGluYWwgb3JnYW5vaWRzIGRlcml2ZWQgZnJvbSBpUFNDIHdoZXJlIHRoZSB0ZXN0aW5nIGNvbmRpdGlvbiBpcyBDUlgtTENBIGFuZCB0aGUgc3BlY2lmaWMgbXV0YXRpb24gaXMgSTEzOGZzNDguCgoyLglNdWx0aXBsZSBoeXBvdGhlc2lzIHRlc3RpbmcgLSBjb3JyZWN0IHlvdXIgcC12YWx1ZXMgdXNpbmcgYSBtdWx0aXBsZSBoeXBvdGhlc2lzIGNvcnJlY3Rpb24gbWV0aG9kLiBXaGljaCBtZXRob2QgZGlkIHlvdSB1c2U/IEFuZCBXaHk/IEhvdyBtYW55IGdlbmVzIHBhc3NlZCBjb3JyZWN0aW9uPwoKICAgIEkgdHJpZWQgdXNpbmcgQmVuamFtbmkgLSBIb2NoYmVyZyBmcm9tIGJvdGggbGltbWEgYW5kIGVkZ2VSIHRvIGNvcnJlY3QgdGhlIHAtdmFsdWUgZm9yIG15IGdlbmVzLiBUaGUgb25lIHRoYXQgSSB3b3VsZCBnbyB3aXRoIGlzIHRoZSBCSCBmcm9tIGxpbW1hIGluIHRoZSBzaW1wbGUgbW9kZWwsIHNpbmNlIHRoZSBzaW1wbGUgbW9kZWwgaXMgYSBsb3QgYmV0dGVyIGluIGxvZyBGQyBhbmQgdGhlIHJlc3VsdCBhcmUgZXhwZWN0ZWQgdW5saWtlIGluIGVkZ2VSIGl0cyBDUlggaXMgYWN0dWFsbHkgdXByZWd1bGF0ZWQgaW5zdGVhZCBvZiBkb3ducmVndWxhdGVkLi4gRm9yIG51bWJlciBvZiBnZW5lcyBwYXNzZWQgY29ycmVjdGlvbiwgbGltbWEgZ2F2ZSBtZSA2MTc0IGFuZCA1Njc3LCBmb3IgdGhlIDIgbW9kZWxzIHJlc3BlY3RpdmVseSwgZWRnZVIgZ2F2ZSBtZSA0MjIyLiBJIHVzZWQgQmVuamFtbmkgLSBIb2NoYmVyZywgYmVjYXVzZSB0aGF0IHNlZW1zIGxpa2UgaXQgaXMgdGhlIG1vc3Qgd2lkZWx5IHVzZWQsIGFuZCB0aGF0IHdhcyB0aGUgZGVmYXVsdCBtZXRob2QgZm9yIG11bHRpcGxlIGh5cG90aGVzaXMgY29ycmVjdGluZyBpbiBib3RoIGxpbW1hIHRvcHRhYmxlIGFuZCBlZGdlUiB0b3B0YWdzLiAKCjMuCVNob3cgdGhlIGFtb3VudCBvZiBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMgdXNpbmcgYW4gTUEgUGxvdCBvciBhIFZvbGNhbm8gcGxvdC4gSGlnaGxpZ2h0IGdlbmVzIG9mIGludGVyZXN0LgoKICAgIHNlZSB2b2xjYW5vIHBsb3Qgc2VjdGlvbgoKNC4JVmlzdWFsaXplIHlvdXIgdG9wIGhpdHMgdXNpbmcgYSBoZWF0bWFwLiBEbyB5b3UgY29uZGl0aW9ucyBjbHVzdGVyIHRvZ2V0aGVyPyBFeHBsYWluIHdoeSBvciB3aHkgbm90LgoKICAgIE15IGNvbmRpdGlvbnMgZG8gbm90IGNsdXN0ZXIgdG9nZXRoZXIgaW4gdGhlIHNlbnNlIHRoYXQgdGhlIHRlc3QgY29uZGl0aW9ucyBhbmQgdGhlIGNvbnRyb2xzIGRvIG5vdC4gSG93ZXZlciwgdGhlIGRheXMgKGUuZy4gZXhwcmVzc2lvbiBvbiBkYXkgOTAgdnMgZXhwcmVzc2lvbiBvbiBkYXkgMjAwIHZzIGV4cHJlc3Npb24gb24gZGF5IDEyNS8xNTApLCBkbyBjbHVzdGVyIHRvZ2V0aGVyLCBJIGJlbGlldmUgdGhpcyBpcyBiZWNhdXNlIGR1cmluZyBkZXZlbG9wbWVudCBtYW55IGdlbmVzIGdldCBleHByZXNzZWQgYW5kIHJlcHJlc3NlZCwgc28gdGhhdCBjaGFuZ2VzIGluIHRoZSBnZW5lcyBleHByZXNzaW9uIGR1ZSB0byBkZXZlbG9wbWVudCBvdmVyc2hhZG93cyB0aG9zZSB0aGF0IGFyZSBkdWUgdG8gdGhlIGRpc2Vhc2UgKExDQSkuIEFzIHdlbGwsIGRheSAxMjUgYW5kIDE1MCBhcmUgdG9vIGNsb3NlIGluIGRldmVsb3BtZW50LCBjb21wYXJlIHRvIHRoZSBvdGhlciB0d28sIHNvIHRoZXJlIGlzIGxlc3Mgb2YgYSBkaWZmZXJlbmNlLiAKCgoxLglXaGF0IG1ldGhvZCBkaWQgeW91IGNob29zZSBhbmQgd2h5PwoKICAgIEkgY2hvb3NlIGc6cHJvZmlsZXIgYXMgdGhlIG1ldGhvZCBmb3Igb3ZlciByZXByZXNlbnRhdGlvbiBhbmFseXNpcywgc2luY2UgSSBhbHJlYWR5IGhhdmUgZXhwZXJpZW5jZSB1c2luZyBpdCBmcm9tIHRoZSBnOnByb2ZpbGVyIGFzc2lnbm1lbnQgKHdoaWNoIG1ha2VzIGl0IGVhc2llciB0byB1c2UpIGFuZCBQcm9mZXNzb3IgSXNzZXJsaW4gc2VlbXMgdG8gcmVjb21tZW5kIGl0IGR1ZSB0byB0aGUgZmFjdCB0aGF0IHNoZSBhc2tlZCB1cyB0byB1c2UgZzpwcm9maWxlciBmb3IgdGhlIGhvbWV3b3JrIGFzc2lnbm1lbnQuIEFzIHdlbGwsIHRoZSBwdmFsdWUgaXMgc2hvd24gdmVyeSBjbGVhcmx5IG9uIGc6cHJvZmlsZXIgd2l0aCBsb3RzIG9mIGNvbG9ycyBhbmQgdGhlIHJlc3VsdCBhcmUgcXVpdGUgaW50ZXJwcmV0YWJsZQoKMi4JV2hhdCBhbm5vdGF0aW9uIGRhdGEgZGlkIHlvdSB1c2UgYW5kIHdoeT8gV2hhdCB2ZXJzaW9uIG9mIHRoZSBhbm5vdGF0aW9uIGFyZSB5b3UgdXNpbmc/CgogICAgSSB1c2VkIEdlbmUgb250b2xvZ3k6IEJpb2xvZ2ljYWwgUHJvY2VzcyByZWxlYXNlZCAyMDIxLTEyLTE1LCBSZWFjdG9tZSByZWxlYXNlZCAyMDIyLTEtMywgV2lraSBQYXRod2F5cyByZWxlYXNlZCAyMDIxLTEyLTEwLiBJIGFtIHVzaW5nIHRoaXMgc2luY2UgdGhpcyB3YXMgcmVjb21tZW5kIGluIHRoZSBnOnByb2ZpbGVyIGFzc2lnbm1lbnQuIAoKMy4JSG93IG1hbnkgZ2VuZXNldHMgd2VyZSByZXR1cm5lZCB3aXRoIHdoYXQgdGhyZXNob2xkcz8KCiAgICBGb3IgZmlyc3QgNTAwMCBnZW5lcyBvbiB0aGUgd2hvbGUgbGlzdCAoSSBjb3VsZCBub3QgcmFuIGFsbCAxOTAwMCBnZW5lcywgc2luY2UgZzpwcm9maWxlciB3aWxsIGNyYXNoKSBpbiBhIG9yZGVyZWQgcXVlcnksIHRoZSBudW1iZXIgb2YgdGVybXMgcmV0dXJuIGF0IGxlc3MgdGhhbiAwLjA1IHB2YWx1ZSBhbmQgaGF2ZSBiZXR3ZWVuIDUtMjAwIGdlbmVzIGlzIDMzIGZvciBHZW5lIG9udG9sb2d5OiBCaW9sb2dpY2FsIFByb2Nlc3MsIDAgZm9yIFJlYWN0b21lLCAwIGZvciBXaWtpIFBhdGh3YXlzCgo0LglSdW4gdGhlIGFuYWx5c2lzIHVzaW5nIHRoZSB1cC1yZWd1bGF0ZWQgc2V0IG9mIGdlbmVzLCBhbmQgdGhlIGRvd24tcmVndWxhdGVkIHNldCBvZiBnZW5lcyBzZXBhcmF0ZWx5LiBIb3cgZG8gdGhlc2UgcmVzdWx0cyBjb21wYXJlIHRvIHVzaW5nIHRoZSB3aG9sZSBsaXN0IChpLmUgYWxsIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcyB0b2dldGhlciB2cy4gdGhlIHVwLXJlZ3VsYXRlZCBhbmQgZG93biByZWd1bGF0ZWQgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzIHNlcGFyYXRlbHkpPwoKICAgIHRoZSBkb3ducmVndWxhdGVkIGlzIHJlYWxseSBzaW1pbGFyIHRvIHRoZSB3aG9sZSBsaXN0CiAgICBGb3IgdGhlIGRvd25yZWd1bGF0ZWQgZ2VuZXMgb3JkZXJlZCBxdWVyeSwgdGhlIG51bWJlciBvZiB0ZXJtcyByZXR1cm4gYXQgbGVzcyB0aGFuIDAuMDUgcHZhbHVlIGFuZCBoYXZlIGJldHdlZW4gNS0yMDAgZ2VuZXMgaXMgMzIgZm9yIEdlbmUgb250b2xvZ3k6IEJpb2xvZ2ljYWwgUHJvY2VzcywgMCBmb3IgUmVhY3RvbWUsIDAgZm9yIFdpa2kgUGF0aHdheXMuCiAgICBGb3IgdGhlIHVwcmVndWxhdGVkIGdlbmVzIG9yZGVyZWQgcXVlcnksIHRoZSBudW1iZXIgb2YgdGVybXMgcmV0dXJuIGF0IGxlc3MgdGhhbiAwLjA1IHB2YWx1ZSBhbmQgaGF2ZSBiZXR3ZWVuIDUtMjAwIGdlbmVzIGlzIDQ0MSBmb3IgR2VuZSBvbnRvbG9neTogQmlvbG9naWNhbCBQcm9jZXNzLCA4IGZvciBSZWFjdG9tZSwgMCBmb3IgV2lraSBQYXRod2F5cy4KCjEuCURvIHRoZSBvdmVyLXJlcHJlc2VudGF0aW9uIHJlc3VsdHMgc3VwcG9ydCBjb25jbHVzaW9ucyBvciBtZWNoYW5pc20gZGlzY3Vzc2VkIGluIHRoZSBvcmlnaW5hbCBwYXBlcj8KCiAgICBJdCBkb2VzLCB0aGUgdG9wIHR3byB0ZXJtIG9mIEdPOkJQIChmb3IgdGVybXMgdGhhdCBoYXZlIGJldHdlZW4gNS0yMDAgZ2VuZXMpIGZvciB0aGUgZG93bnJlZ3VsYXRlZCBvcmRlciBnZW5lcyBpcyBkZXRlY3Rpb24gb2YgbGlnaHQgc3RpbXVsdXMgYW5kIHBob3RvcmVjZXB0b3IgY2VsbCBkaWZmZXJlbnRpdGFpb24uCiAgICBhbmQgdGhlIGZpcnN0IGhhbGYgb2YgdGhlIHRlcm1zIGFsbCBoYXZlIHRvIGRvIHdpdGggdmlzaW9uCgogICAgQXMgd2VsbCwgdGhlIHRvcCB0d28gdGVybSAoZm9yIHRlcm1zIHRoYXQgaGF2ZSBiZXR3ZWVuIDUtMjAwIGdlbmVzKSBmb3IgdGhlIGZpcnN0IDUwMDAgZ2VuZXMgb24gdGhlIHdob2xlIGxpc3QgaXMgZGV0ZWN0aW9uIG9mIGxpZ2h0IHN0aW11bHVzIGFuZCBwaG90b3JlY2VwdG9yIGNlbGwgZGlmZmVyZW50aXRhaW9uLiBBbmQgdGhlIGZpcnN0IGhhbGYgb2YgdGhlIHRlcm1zIGFsbCBoYXZlIHRvIGRvIHdpdGggdmlzaW9uCgogICAgU2luY2UgYSBtdXRhdGlvbiBpbiBDUlggcHJldmVudHMgaXQgZnJvbSBiZWluZyB0cmFuc2NyaWJlZCwgdGhlIHJlc3VsdCBvZiB1cC1yZWd1bGF0ZWQgZ2VuZXMgaXMgbm90IGluIHRoZSBzY29wZSBvZiBleHBlcmltZW50IGFueXdheS4gCgoyLglDYW4geW91IGZpbmQgZXZpZGVuY2UsIGkuZS4gcHVibGljYXRpb25zLCB0byBzdXBwb3J0IHNvbWUgb2YgdGhlIHJlc3VsdHMgdGhhdCB5b3Ugc2VlLiBIb3cgZG9lcyB0aGlzIGV2aWRlbmNlIHN1cHBvcnQgeW91ciByZXN1bHRzLgoKICAgIFllcywgaW4gW0BwaG90b3JlY2VwdG9yXSwgc2F5IHRoYXQgQ1JYIGlzIGNlbnRyYWwgdG8gdGhlIHRyYW5zY3JpcHRpb24gZmFjdG9yIG5ldHdvcmsgdGhhdCBjb250cm9scyBwaG90b3JlY2VwdG9yIGRldmVsb3BtZW50LiBJbiBib3RoIHRoZSB3aG9sZSBsaXN0IG9yZGVyZWQgaW4gdGhlIGRvd25yZWd1bGF0ZWQgZ2VuZXMsIHBob3RvcmVjZXB0aW9uIGlzIGFuIGltcG9ydGFudCB0ZXJtLCB0aGlzIG1lYW5zIHRoYXQgaW4gdGhlIFJOQSBleHByZXNzaW9uIG9mIHRoZXNlIGdlbmVzLCBwaG90b3RyYW5zZHVjdGlvbiBpcyBub3QgYWN0aXZhdGVkIGR1ZSB0byBDUljigJlzIHJvbGUsIHdoaWNoIGlzIGNvbnNpc3RlbnQgdG8gdGhlIGZpbmRpbmcgcHJlc2VudGVkIGJ5IHRoYXQgc3R1ZHkuIE1vcmVvdmVyLCBpdCBpcyBhY3R1YWxseSBwaG90b3JlY2VwdG9yIGNlbGwgZGlmZmVyZW50aWF0aW9uLCB3aGljaCBpcyBleGFjdGx5IHdoYXQgQ1JYIGNvbnRyb2xzLiAKCiAgICBJIGJlbGlldmUgdGhhdCBkZXRlY3Rpb24gb2YgbGlnaHQgc3RpbXVsdXMgaXMgYWxzbyBzdXBwb3J0ZWQgYnkgdGhpcywgc2luY2UgbGlnaHQgc3RpbXVsdXMgaXMgZXNzZW50aWFsbHkgcGhvdG9ucywgYW5kIHBob3RvcmVjZXB0b3IgYXJlIGFjdGl2YXRlZCBieSBwaG90b25zLgoKCiMgSm91cm5hbCBMaW5rCgpodHRwczovL2dpdGh1Yi5jb20vYmNiNDIwLTIwMjIvS2FpcmVuX0NoZW4vd2lraS9Kb3VybmFsLTc6LURpZmZlcmVudGlhbC1HZW5lLWV4cHJlc3Npb24tYW5kLVByZWxpbWluYXJ5LU9SQQoKIyBSZWZlcmVuY2Vz